import 'package:flutter/material.dart'; import 'app_colors.dart'; import 'app_data_styles.dart'; import 'status_colors.dart'; /// Builds the app's light/dark [ThemeData]. Colors are constructed /// explicitly from [AppColors] rather than via [ColorScheme.fromSeed] so the /// approved mockup's exact hex values can't drift through algorithmic tone /// generation. abstract final class AppTheme { static ThemeData light() => _build(brightness: Brightness.light); static ThemeData dark() => _build(brightness: Brightness.dark); static ThemeData _build({required Brightness brightness}) { final isDark = brightness == Brightness.dark; final colorScheme = isDark ? const ColorScheme.dark( surface: AppColors.darkBg, onSurface: AppColors.darkText, surfaceContainerHighest: AppColors.darkSurfaceAlt, primary: AppColors.darkAccent, onPrimary: AppColors.darkAccentInk, secondary: AppColors.darkAccentStrong, onSecondary: AppColors.darkAccentInk, error: AppColors.critical, onError: Colors.white, outline: AppColors.darkBorder, outlineVariant: AppColors.darkBorder, ) : const ColorScheme.light( surface: AppColors.lightBg, onSurface: AppColors.lightText, surfaceContainerHighest: AppColors.lightSurfaceAlt, primary: AppColors.lightAccent, onPrimary: AppColors.lightAccentInk, secondary: AppColors.lightAccentStrong, onSecondary: AppColors.lightAccentInk, error: AppColors.critical, onError: Colors.white, outline: AppColors.lightBorder, outlineVariant: AppColors.lightBorder, ); final surfaceCard = isDark ? AppColors.darkSurface : AppColors.lightSurface; final textColor = isDark ? AppColors.darkText : AppColors.lightText; final textMuted = isDark ? AppColors.darkTextMuted : AppColors.lightTextMuted; final textFaint = isDark ? AppColors.darkTextFaint : AppColors.lightTextFaint; final border = isDark ? AppColors.darkBorder : AppColors.lightBorder; final textTheme = TextTheme( displaySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 30, color: textColor, letterSpacing: -0.2), headlineSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 22, color: textColor, letterSpacing: -0.1), titleLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 17, color: textColor, letterSpacing: -0.1), titleMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 14, color: textColor), titleSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.6), bodyLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 15, color: textColor, height: 1.5), bodyMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 13.5, color: textColor, height: 1.45), bodySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w500, fontSize: 12, color: textMuted), labelLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13, color: textColor), labelMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 12, color: textMuted), labelSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5), ); return ThemeData( useMaterial3: true, brightness: brightness, colorScheme: colorScheme, scaffoldBackgroundColor: colorScheme.surface, canvasColor: colorScheme.surface, dividerColor: border, fontFamily: 'Inter', textTheme: textTheme, cardTheme: CardThemeData( color: surfaceCard, elevation: 0, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), side: BorderSide(color: border), ), ), appBarTheme: AppBarTheme( backgroundColor: colorScheme.surface, foregroundColor: textColor, elevation: 0, surfaceTintColor: Colors.transparent, ), navigationRailTheme: NavigationRailThemeData( backgroundColor: isDark ? AppColors.darkSurfaceAlt : AppColors.lightSurfaceAlt, indicatorColor: colorScheme.primary.withValues(alpha: 0.16), selectedIconTheme: IconThemeData(color: colorScheme.secondary), unselectedIconTheme: IconThemeData(color: textMuted), selectedLabelTextStyle: TextStyle(color: colorScheme.secondary, fontWeight: FontWeight.w700, fontSize: 12.5), unselectedLabelTextStyle: TextStyle(color: textMuted, fontWeight: FontWeight.w500, fontSize: 12.5), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: colorScheme.primary, foregroundColor: colorScheme.onPrimary, elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: textColor, side: BorderSide(color: border), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: colorScheme.surface, contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), border: OutlineInputBorder( borderRadius: BorderRadius.circular(7), borderSide: BorderSide(color: border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(7), borderSide: BorderSide(color: border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(7), borderSide: BorderSide(color: colorScheme.primary, width: 1.5), ), labelStyle: TextStyle(color: textMuted, fontSize: 12.5, fontWeight: FontWeight.w600), ), dataTableTheme: DataTableThemeData( headingTextStyle: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5), dataTextStyle: TextStyle(fontFamily: 'Inter', fontSize: 13, color: textColor), dividerThickness: 1, ), extensions: [ isDark ? StatusColors.dark : StatusColors.light, AppDataStyles.resolve(textColor: textColor, mutedColor: textMuted), ], ); } }