import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'app_typography.dart'; import 'app_surfaces.dart'; class AppTheme { static ThemeData light() { final base = ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF334155), brightness: Brightness.light, ), useMaterial3: true, ); 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 = AppSurfaces( cardRadius: 16, compactCardRadius: 12, dialogRadius: 20, cardElevation: 3, cardShadowColor: const Color.fromRGBO(0, 0, 0, 0.12), compactShadowColor: const Color.fromRGBO(0, 0, 0, 0.08), ); return base.copyWith( textTheme: textTheme, scaffoldBackgroundColor: base.colorScheme.surfaceContainerLowest, extensions: [mono, surfaces], appBarTheme: AppBarTheme( backgroundColor: base.colorScheme.surface, foregroundColor: base.colorScheme.onSurface, elevation: 0, scrolledUnderElevation: 1, surfaceTintColor: base.colorScheme.surfaceTint, centerTitle: false, titleTextStyle: textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, letterSpacing: 0.2, ), ), cardTheme: CardThemeData( color: base.colorScheme.surface, elevation: 3, // M2-style elevation for visible separation (2-4 allowed) margin: EdgeInsets.zero, shadowColor: const Color.fromRGBO(0, 0, 0, 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: base.colorScheme.outlineVariant, width: 1), ), ), chipTheme: ChipThemeData( backgroundColor: base.colorScheme.surfaceContainerHighest, side: BorderSide(color: base.colorScheme.outlineVariant), labelStyle: textTheme.labelSmall, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), dividerTheme: DividerThemeData( color: base.colorScheme.outlineVariant, thickness: 1, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: base.colorScheme.surfaceContainerLow, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.outlineVariant), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.outlineVariant), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.primary, width: 1.5), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), side: BorderSide(color: base.colorScheme.outline), padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), ), ), navigationDrawerTheme: NavigationDrawerThemeData( backgroundColor: base.colorScheme.surface, indicatorColor: base.colorScheme.secondaryContainer, tileHeight: 52, ), navigationRailTheme: NavigationRailThemeData( backgroundColor: base.colorScheme.surface, selectedIconTheme: IconThemeData(color: base.colorScheme.primary), selectedLabelTextStyle: textTheme.labelLarge?.copyWith( fontWeight: FontWeight.w600, ), unselectedIconTheme: IconThemeData(color: base.colorScheme.onSurface), indicatorColor: base.colorScheme.secondaryContainer, ), navigationBarTheme: NavigationBarThemeData( backgroundColor: base.colorScheme.surface, indicatorColor: base.colorScheme.primaryContainer, labelTextStyle: WidgetStateProperty.all( textTheme.labelMedium?.copyWith(fontWeight: FontWeight.w600), ), ), listTileTheme: ListTileThemeData( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), tileColor: base.colorScheme.surface, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), ), ); } static ThemeData dark() { final base = ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF334155), brightness: Brightness.dark, ), useMaterial3: true, ); 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 = AppSurfaces( cardRadius: 16, compactCardRadius: 12, dialogRadius: 20, cardElevation: 3, cardShadowColor: const Color.fromRGBO(0, 0, 0, 0.24), compactShadowColor: const Color.fromRGBO(0, 0, 0, 0.12), ); return base.copyWith( textTheme: textTheme, scaffoldBackgroundColor: base.colorScheme.surface, extensions: [mono, surfaces], appBarTheme: AppBarTheme( backgroundColor: base.colorScheme.surface, foregroundColor: base.colorScheme.onSurface, elevation: 0, scrolledUnderElevation: 1, surfaceTintColor: base.colorScheme.surfaceTint, centerTitle: false, titleTextStyle: textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, letterSpacing: 0.2, ), ), cardTheme: CardThemeData( color: base.colorScheme.surfaceContainer, elevation: 3, // M2-style elevation for visible separation (2-4 allowed) margin: EdgeInsets.zero, shadowColor: const Color.fromRGBO(0, 0, 0, 0.24), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: base.colorScheme.outlineVariant, width: 1), ), ), chipTheme: ChipThemeData( backgroundColor: base.colorScheme.surfaceContainerHighest, side: BorderSide(color: base.colorScheme.outlineVariant), labelStyle: textTheme.labelSmall, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), dividerTheme: DividerThemeData( color: base.colorScheme.outlineVariant, thickness: 1, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: base.colorScheme.surfaceContainerLow, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.outlineVariant), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.outlineVariant), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: base.colorScheme.primary, width: 1.5), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), side: BorderSide(color: base.colorScheme.outline), padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), ), ), navigationDrawerTheme: NavigationDrawerThemeData( backgroundColor: base.colorScheme.surface, indicatorColor: base.colorScheme.secondaryContainer, tileHeight: 52, ), navigationRailTheme: NavigationRailThemeData( backgroundColor: base.colorScheme.surface, selectedIconTheme: IconThemeData(color: base.colorScheme.primary), selectedLabelTextStyle: textTheme.labelLarge?.copyWith( fontWeight: FontWeight.w600, ), unselectedIconTheme: IconThemeData(color: base.colorScheme.onSurface), indicatorColor: base.colorScheme.secondaryContainer, ), navigationBarTheme: NavigationBarThemeData( backgroundColor: base.colorScheme.surface, indicatorColor: base.colorScheme.primaryContainer, labelTextStyle: WidgetStateProperty.all( textTheme.labelMedium?.copyWith(fontWeight: FontWeight.w600), ), ), listTileTheme: ListTileThemeData( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), tileColor: base.colorScheme.surfaceContainer, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), ), ); } }