Files

197 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// NetDesk design tokens + [ThemeData].
///
/// A single, dark, premium theme in the spirit of TeamViewer / RustDesk —
/// deep near-black surfaces, a confident green accent, generous spacing and
/// softly rounded cards. All colors live here as tokens so the rest of the
/// app never hardcodes a hex value.
class NetDeskColors {
NetDeskColors._();
/// App background — deep, near-black with a cool blue cast.
static const Color background = Color(0xFF0B0F15);
/// Raised surface (cards, panels) — one step lighter than the background.
static const Color surface = Color(0xFF121821);
/// A second elevation used for inputs / inset wells.
static const Color surfaceAlt = Color(0xFF0E141C);
/// Hairline borders and dividers.
static const Color border = Color(0xFF1E2733);
/// Primary brand accent — a vivid, trustworthy green.
static const Color accent = Color(0xFF22C55E);
/// Darker accent for pressed / gradient ends.
static const Color accentDeep = Color(0xFF16A34A);
/// Soft accent glow used sparingly behind the wordmark.
static const Color accentGlow = Color(0x3322C55E);
/// High-emphasis text.
static const Color textPrimary = Color(0xFFF2F5F9);
/// Medium-emphasis text (subtitles, labels).
static const Color textSecondary = Color(0xFF9AA7B8);
/// Low-emphasis text (hints, captions).
static const Color textMuted = Color(0xFF5E6B7D);
/// Destructive / disconnect actions.
static const Color danger = Color(0xFFEF4444);
}
class NetDeskRadius {
NetDeskRadius._();
static const double sm = 8;
static const double md = 12;
static const double lg = 16;
static const double xl = 22;
}
class NetDeskSpacing {
NetDeskSpacing._();
static const double xs = 6;
static const double sm = 10;
static const double md = 16;
static const double lg = 24;
static const double xl = 36;
}
class NetDeskTheme {
NetDeskTheme._();
static ThemeData get dark {
const scheme = ColorScheme.dark(
primary: NetDeskColors.accent,
onPrimary: Color(0xFF04210F),
secondary: NetDeskColors.accentDeep,
onSecondary: Color(0xFF04210F),
surface: NetDeskColors.surface,
onSurface: NetDeskColors.textPrimary,
error: NetDeskColors.danger,
onError: Colors.white,
outline: NetDeskColors.border,
);
final base = ThemeData.dark(useMaterial3: true);
return base.copyWith(
colorScheme: scheme,
scaffoldBackgroundColor: NetDeskColors.background,
canvasColor: NetDeskColors.background,
dividerColor: NetDeskColors.border,
splashFactory: InkSparkle.splashFactory,
textTheme: base.textTheme
.apply(
bodyColor: NetDeskColors.textPrimary,
displayColor: NetDeskColors.textPrimary,
fontFamily: 'Roboto',
)
.copyWith(
bodyMedium: const TextStyle(
color: NetDeskColors.textSecondary,
height: 1.4,
),
labelLarge: const TextStyle(
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
),
appBarTheme: const AppBarTheme(
backgroundColor: NetDeskColors.surfaceAlt,
foregroundColor: NetDeskColors.textPrimary,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: NetDeskColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
systemOverlayStyle: SystemUiOverlayStyle.light,
),
cardTheme: CardThemeData(
color: NetDeskColors.surface,
elevation: 0,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
side: const BorderSide(color: NetDeskColors.border),
borderRadius: BorderRadius.circular(NetDeskRadius.xl),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: NetDeskColors.surfaceAlt,
hintStyle: const TextStyle(color: NetDeskColors.textMuted),
labelStyle: const TextStyle(color: NetDeskColors.textSecondary),
contentPadding: const EdgeInsets.symmetric(
horizontal: NetDeskSpacing.md,
vertical: 18,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(NetDeskRadius.md),
borderSide: const BorderSide(color: NetDeskColors.border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(NetDeskRadius.md),
borderSide: const BorderSide(color: NetDeskColors.accent, width: 1.6),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(NetDeskRadius.md),
borderSide: const BorderSide(color: NetDeskColors.danger),
),
),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
backgroundColor: NetDeskColors.accent,
foregroundColor: const Color(0xFF04210F),
disabledBackgroundColor: NetDeskColors.border,
disabledForegroundColor: NetDeskColors.textMuted,
minimumSize: const Size.fromHeight(54),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(NetDeskRadius.md),
),
),
),
iconButtonTheme: IconButtonThemeData(
style: IconButton.styleFrom(
foregroundColor: NetDeskColors.textSecondary,
hoverColor: NetDeskColors.surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(NetDeskRadius.sm),
),
),
),
tooltipTheme: const TooltipThemeData(
decoration: BoxDecoration(
color: Color(0xFF1B2430),
borderRadius: BorderRadius.all(Radius.circular(NetDeskRadius.sm)),
),
textStyle: TextStyle(color: NetDeskColors.textPrimary, fontSize: 12),
),
dividerTheme: const DividerThemeData(
color: NetDeskColors.border,
thickness: 1,
space: 1,
),
);
}
}