import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'app_typography.dart'; import 'app_surfaces.dart'; import 'app_status_colors.dart'; /// Refined enterprise **Cyber/IT** theme for TasQ, built on Material 3. /// /// Identity: /// * Deep slate surfaces (dark) / cool near-white (light) with an electric /// **azure** primary, **teal** secondary and **violet** tertiary. /// * Cards use a flat tonal fill plus a hairline outline (no drop shadow) for a /// precise, technical feel. /// * A tightened 12–16 dp shape language (see [AppSurfaces]). /// * Monospace ([AppMonoText]) reserved for technical data — IDs, IPs, ports, /// timestamps — and chip labels. /// * Semantic status tokens ([AppStatusColors]) drive all status/priority color. class AppTheme { const AppTheme._(); /// Brand azure — anchors M3's neutral tonal ramp generation. static const Color _seed = Color(0xFF2D7FF9); static const AppSurfaces _surfaces = AppSurfaces( cardRadius: 12, compactCardRadius: 10, containerRadius: 16, dialogRadius: 20, chipRadius: 8, ); // ──────────────────────────────────────────────────────────── // COLOR SCHEMES (hand-tuned cyber palette) // ──────────────────────────────────────────────────────────── static ColorScheme _darkScheme() { return ColorScheme.fromSeed( seedColor: _seed, brightness: Brightness.dark, ).copyWith( primary: const Color(0xFF4EA8FF), onPrimary: const Color(0xFF04263F), primaryContainer: const Color(0xFF0E3A5F), onPrimaryContainer: const Color(0xFFCFE7FF), secondary: const Color(0xFF2DD4BF), onSecondary: const Color(0xFF00201C), secondaryContainer: const Color(0xFF0C4B45), onSecondaryContainer: const Color(0xFFB8F5EC), tertiary: const Color(0xFFA78BFA), onTertiary: const Color(0xFF1C0E3F), tertiaryContainer: const Color(0xFF3A2A6B), onTertiaryContainer: const Color(0xFFE7DDFF), error: const Color(0xFFF87171), onError: const Color(0xFF2A0A0A), errorContainer: const Color(0xFF5A1A1A), onErrorContainer: const Color(0xFFFFC9C4), surface: const Color(0xFF0E141B), onSurface: const Color(0xFFE3E9F0), onSurfaceVariant: const Color(0xFFA7B4C2), surfaceContainerLowest: const Color(0xFF080C11), surfaceContainerLow: const Color(0xFF141B23), surfaceContainer: const Color(0xFF17202A), surfaceContainerHigh: const Color(0xFF1E2833), surfaceContainerHighest: const Color(0xFF26313D), surfaceDim: const Color(0xFF0B0F14), surfaceBright: const Color(0xFF2A3542), outline: const Color(0xFF3A4A5A), outlineVariant: const Color(0xFF283440), surfaceTint: const Color(0xFF4EA8FF), inverseSurface: const Color(0xFFE3E9F0), onInverseSurface: const Color(0xFF111820), inversePrimary: const Color(0xFF0B6BCB), ); } static ColorScheme _lightScheme() { return ColorScheme.fromSeed( seedColor: _seed, brightness: Brightness.light, ).copyWith( primary: const Color(0xFF0B6BCB), onPrimary: const Color(0xFFFFFFFF), primaryContainer: const Color(0xFFD3E7FF), onPrimaryContainer: const Color(0xFF00294A), secondary: const Color(0xFF0F766E), onSecondary: const Color(0xFFFFFFFF), secondaryContainer: const Color(0xFFB8F1E8), onSecondaryContainer: const Color(0xFF00201C), tertiary: const Color(0xFF6D28D9), onTertiary: const Color(0xFFFFFFFF), tertiaryContainer: const Color(0xFFE9DDFF), onTertiaryContainer: const Color(0xFF23005C), error: const Color(0xFFDC2626), onError: const Color(0xFFFFFFFF), errorContainer: const Color(0xFFFBD9D6), onErrorContainer: const Color(0xFF450A0A), surface: const Color(0xFFF6F8FB), onSurface: const Color(0xFF111820), onSurfaceVariant: const Color(0xFF42505F), surfaceContainerLowest: const Color(0xFFFFFFFF), surfaceContainerLow: const Color(0xFFF0F4F9), surfaceContainer: const Color(0xFFEDF1F6), surfaceContainerHigh: const Color(0xFFE4EAF2), surfaceContainerHighest: const Color(0xFFDCE4EE), surfaceDim: const Color(0xFFD7DEE8), surfaceBright: const Color(0xFFF6F8FB), outline: const Color(0xFF73808F), outlineVariant: const Color(0xFFC3CDDA), surfaceTint: const Color(0xFF0B6BCB), inverseSurface: const Color(0xFF111820), onInverseSurface: const Color(0xFFEFF3F8), inversePrimary: const Color(0xFFA6C8FF), ); } // ──────────────────────────────────────────────────────────── // LIGHT // ──────────────────────────────────────────────────────────── static ThemeData light() { return _apply( ThemeData(colorScheme: _lightScheme(), useMaterial3: true), AppStatusColors.light, ); } // ──────────────────────────────────────────────────────────── // DARK // ──────────────────────────────────────────────────────────── static ThemeData dark() { return _apply( ThemeData(colorScheme: _darkScheme(), useMaterial3: true), AppStatusColors.dark, ); } // ──────────────────────────────────────────────────────────── // SHARED BUILDER // ──────────────────────────────────────────────────────────── static ThemeData _apply(ThemeData base, AppStatusColors status) { final cs = base.colorScheme; final isDark = cs.brightness == Brightness.dark; final textTheme = GoogleFonts.spaceGroteskTextTheme(base.textTheme); final monoTheme = GoogleFonts.robotoMonoTextTheme(base.textTheme); final mono = AppMonoText( label: monoTheme.labelMedium?.copyWith(letterSpacing: 0.3) ?? const TextStyle(letterSpacing: 0.3), body: monoTheme.bodyMedium?.copyWith(letterSpacing: 0.2) ?? const TextStyle(letterSpacing: 0.2), ); final surfaces = _surfaces; // Card fill: flat tonal surface + hairline outline (cyber "panel" look). final cardColor = isDark ? cs.surfaceContainer : cs.surfaceContainerLowest; return base.copyWith( textTheme: textTheme, scaffoldBackgroundColor: isDark ? const Color(0xFF0B0F14) : cs.surfaceContainerLowest, extensions: [mono, surfaces, status], // ── AppBar ────────────────────────────────────────────── appBarTheme: AppBarTheme( backgroundColor: cs.surface, foregroundColor: cs.onSurface, elevation: 0, scrolledUnderElevation: 2, surfaceTintColor: cs.surfaceTint, centerTitle: false, toolbarHeight: 64, titleSpacing: 16.0, iconTheme: IconThemeData(color: cs.onSurfaceVariant, size: 24), actionsIconTheme: IconThemeData(color: cs.onSurfaceVariant, size: 24), titleTextStyle: textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, letterSpacing: 0.2, ), ), // ── Cards — flat tonal fill + hairline outline (no hard shadow) ── cardTheme: CardThemeData( color: cardColor, elevation: 0, margin: EdgeInsets.zero, shadowColor: Colors.transparent, surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), side: BorderSide(color: cs.outlineVariant), ), ), // ── Chips — mono label, tight radius ──────────────────── chipTheme: ChipThemeData( backgroundColor: cs.surfaceContainerHighest, side: BorderSide(color: cs.outlineVariant), labelStyle: mono.label, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.chipRadius), ), ), // ── Dividers ──────────────────────────────────────────── dividerTheme: DividerThemeData(color: cs.outlineVariant, thickness: 1), // ── Input Fields ──────────────────────────────────────── inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: isDark ? cs.surfaceContainerLow : cs.surfaceContainer, border: OutlineInputBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), borderSide: BorderSide(color: cs.outlineVariant), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), borderSide: BorderSide(color: cs.outlineVariant), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), borderSide: BorderSide(color: cs.primary, width: 2), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), // ── Buttons — M3 hierarchy, tightened shapes ──────────── filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), textStyle: textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w600), ), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: cs.surfaceContainerHigh, foregroundColor: cs.primary, elevation: 0, shadowColor: Colors.transparent, surfaceTintColor: Colors.transparent, side: BorderSide(color: cs.outlineVariant), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), textStyle: textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w600), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), side: BorderSide(color: cs.outline), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), textStyle: textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w600), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), textStyle: textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w600), ), ), iconButtonTheme: IconButtonThemeData( style: IconButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.compactCardRadius), ), ), ), segmentedButtonTheme: SegmentedButtonThemeData( style: ButtonStyle( shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), ), ), ), // ── FAB ────────────────────────────────────────────────── floatingActionButtonTheme: FloatingActionButtonThemeData( backgroundColor: cs.primaryContainer, foregroundColor: cs.onPrimaryContainer, elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.containerRadius), ), ), // ── Navigation — pill indicators ──────────────────────── navigationDrawerTheme: NavigationDrawerThemeData( backgroundColor: cs.surface, indicatorColor: cs.secondaryContainer, tileHeight: 56, indicatorShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.containerRadius), ), ), navigationRailTheme: NavigationRailThemeData( backgroundColor: cs.surface, selectedIconTheme: IconThemeData(color: cs.onSecondaryContainer), unselectedIconTheme: IconThemeData(color: cs.onSurfaceVariant), selectedLabelTextStyle: textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w700, color: cs.onSurface, ), unselectedLabelTextStyle: textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), indicatorColor: cs.secondaryContainer, indicatorShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.containerRadius), ), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: cs.surfaceContainer, indicatorColor: cs.secondaryContainer, indicatorShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), labelTextStyle: WidgetStateProperty.resolveWith((states) { final selected = states.contains(WidgetState.selected); return textTheme.labelMedium?.copyWith( fontWeight: selected ? FontWeight.w700 : FontWeight.w500, color: selected ? cs.onSurface : cs.onSurfaceVariant, ); }), elevation: 2, surfaceTintColor: cs.surfaceTint, ), // ── List Tiles ────────────────────────────────────────── listTileTheme: ListTileThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.cardRadius), ), tileColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), ), // ── Dialogs ───────────────────────────────────────────── dialogTheme: DialogThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.dialogRadius), ), surfaceTintColor: Colors.transparent, backgroundColor: isDark ? cs.surfaceContainerHigh : cs.surfaceContainerLowest, ), // ── Bottom Sheets ─────────────────────────────────────── bottomSheetTheme: BottomSheetThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(surfaces.dialogRadius), ), ), backgroundColor: isDark ? cs.surfaceContainerHigh : cs.surfaceContainerLowest, surfaceTintColor: Colors.transparent, showDragHandle: true, ), // ── Snackbar ──────────────────────────────────────────── snackBarTheme: SnackBarThemeData( behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.compactCardRadius), ), ), // ── Search Bar ────────────────────────────────────────── searchBarTheme: SearchBarThemeData( elevation: WidgetStateProperty.all(0), backgroundColor: WidgetStateProperty.all(cs.surfaceContainerHigh), shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.containerRadius), ), ), ), // ── Tooltips ──────────────────────────────────────────── tooltipTheme: TooltipThemeData( decoration: BoxDecoration( color: cs.inverseSurface, borderRadius: BorderRadius.circular(surfaces.chipRadius), ), textStyle: textTheme.bodySmall?.copyWith(color: cs.onInverseSurface), ), // ── Tab Bar ───────────────────────────────────────────── tabBarTheme: TabBarThemeData( labelStyle: textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w700), unselectedLabelStyle: textTheme.labelLarge, indicatorColor: cs.primary, labelColor: cs.primary, unselectedLabelColor: cs.onSurfaceVariant, indicatorSize: TabBarIndicatorSize.label, dividerColor: cs.outlineVariant, ), // ── PopupMenu ─────────────────────────────────────────── popupMenuTheme: PopupMenuThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(surfaces.compactCardRadius), ), surfaceTintColor: Colors.transparent, color: isDark ? cs.surfaceContainerHigh : cs.surfaceContainerLowest, ), ); } }