import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter_animate/flutter_animate.dart'; class AppColors { // Brand colors (stable across light/dark) static const primary = Color(0xFF1A56DB); static const primaryLight = Color(0xFF3B82F6); static const primaryDark = Color(0xFF1E40AF); static const accent = Color(0xFF00C9A7); static const accentLight = Color(0xFF6EE7B7); static const error = Color(0xFFEF4444); static const warning = Color(0xFFF59E0B); static const success = Color(0xFF10B981); static const gradientStart = Color(0xFF1A56DB); static const gradientEnd = Color(0xFF00C9A7); static const chatUserBubble = Color(0xFF1A56DB); static const scanOverlay = Color(0x801A56DB); // Light palette (raw values) static const _lightBackground = Color(0xFFF8FAFC); static const _lightSurface = Color(0xFFFFFFFF); static const _lightTextPrimary = Color(0xFF1E293B); static const _lightTextSecondary = Color(0xFF64748B); static const _lightTextTertiary = Color(0xFF94A3B8); static const _lightDivider = Color(0xFFE2E8F0); static const _lightCardShadow = Color(0x1A000000); static const _lightChatAiBubble = Color(0xFFF1F5F9); // Dark palette (raw values) static const _darkBackground = Color(0xFF0F172A); static const _darkSurface = Color(0xFF1E293B); static const _darkTextPrimary = Color(0xFFE2E8F0); static const _darkTextSecondary = Color(0xFF94A3B8); static const _darkTextTertiary = Color(0xFF64748B); static const _darkDivider = Color(0xFF334155); static const _darkCardShadow = Color(0x40000000); static const _darkChatAiBubble = Color(0xFF1E293B); // Mutable references — flipped at runtime via applyMode. static Color background = _lightBackground; static Color surface = _lightSurface; static Color textPrimary = _lightTextPrimary; static Color textSecondary = _lightTextSecondary; static Color textTertiary = _lightTextTertiary; static Color divider = _lightDivider; static Color cardShadow = _lightCardShadow; static Color chatAiBubble = _lightChatAiBubble; static bool isDark = false; static void applyMode(bool dark) { isDark = dark; if (dark) { background = _darkBackground; surface = _darkSurface; textPrimary = _darkTextPrimary; textSecondary = _darkTextSecondary; textTertiary = _darkTextTertiary; divider = _darkDivider; cardShadow = _darkCardShadow; chatAiBubble = _darkChatAiBubble; } else { background = _lightBackground; surface = _lightSurface; textPrimary = _lightTextPrimary; textSecondary = _lightTextSecondary; textTertiary = _lightTextTertiary; divider = _lightDivider; cardShadow = _lightCardShadow; chatAiBubble = _lightChatAiBubble; } } } class AppTheme { static ThemeData get theme { final dark = AppColors.isDark; return ThemeData( useMaterial3: true, brightness: dark ? Brightness.dark : Brightness.light, colorScheme: dark ? ColorScheme.dark( primary: AppColors.primaryLight, onPrimary: Colors.white, secondary: AppColors.accent, onSecondary: Colors.white, surface: AppColors.surface, onSurface: AppColors.textPrimary, error: AppColors.error, onError: Colors.white, background: AppColors.background, onBackground: AppColors.textPrimary, ) : ColorScheme.light( primary: AppColors.primary, onPrimary: Colors.white, secondary: AppColors.accent, onSecondary: Colors.white, surface: AppColors.surface, onSurface: AppColors.textPrimary, error: AppColors.error, onError: Colors.white, background: AppColors.background, onBackground: AppColors.textPrimary, ), scaffoldBackgroundColor: AppColors.background, textTheme: GoogleFonts.notoSansScTextTheme().copyWith( displayLarge: GoogleFonts.notoSansSc( fontSize: 32, fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), displayMedium: GoogleFonts.notoSansSc( fontSize: 24, fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), titleLarge: GoogleFonts.notoSansSc( fontSize: 20, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), titleMedium: GoogleFonts.notoSansSc( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), bodyLarge: GoogleFonts.notoSansSc( fontSize: 16, color: AppColors.textPrimary, ), bodyMedium: GoogleFonts.notoSansSc( fontSize: 14, color: AppColors.textSecondary, ), labelLarge: GoogleFonts.notoSansSc( fontSize: 14, fontWeight: FontWeight.w500, ), ), appBarTheme: AppBarTheme( elevation: 0, centerTitle: true, backgroundColor: AppColors.surface, foregroundColor: AppColors.textPrimary, systemOverlayStyle: dark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, titleTextStyle: GoogleFonts.notoSansSc( fontSize: 18, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), cardTheme: CardThemeData( elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), color: AppColors.surface, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: AppColors.background, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.error, width: 1), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), hintStyle: GoogleFonts.notoSansSc( fontSize: 14, color: AppColors.textTertiary, ), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), backgroundColor: AppColors.primary, foregroundColor: Colors.white, textStyle: GoogleFonts.notoSansSc( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), side: const BorderSide(color: AppColors.primary), foregroundColor: AppColors.primary, textStyle: GoogleFonts.notoSansSc( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: AppColors.primary, textStyle: GoogleFonts.notoSansSc( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), floatingActionButtonTheme: const FloatingActionButtonThemeData( backgroundColor: AppColors.primary, foregroundColor: Colors.white, elevation: 4, ), bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: AppColors.surface, selectedItemColor: AppColors.primary, unselectedItemColor: AppColors.textTertiary, type: BottomNavigationBarType.fixed, elevation: 8, ), chipTheme: ChipThemeData( backgroundColor: AppColors.background, selectedColor: AppColors.primary.withOpacity(0.1), labelStyle: GoogleFonts.notoSansSc(fontSize: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), dividerTheme: DividerThemeData( color: AppColors.divider, thickness: 1, space: 1, ), tabBarTheme: TabBarThemeData( labelColor: AppColors.primary, unselectedLabelColor: AppColors.textSecondary, labelStyle: GoogleFonts.notoSansSc( fontSize: 14, fontWeight: FontWeight.w600, ), unselectedLabelStyle: GoogleFonts.notoSansSc( fontSize: 14, fontWeight: FontWeight.w400, ), indicatorSize: TabBarIndicatorSize.tab, ), ); } } class AppGradients { // Light: bright brand blue → teal. Dark: deeper, more subdued. static const _lightStart = Color(0xFF1A56DB); static const _lightEnd = Color(0xFF00C9A7); static const _darkStart = Color(0xFF1E3A8A); static const _darkEnd = Color(0xFF0F766E); static LinearGradient get primary => LinearGradient( colors: AppColors.isDark ? const [_darkStart, _darkEnd] : const [_lightStart, _lightEnd], begin: Alignment.topLeft, end: Alignment.bottomRight, ); static const card = LinearGradient( colors: [Color(0xFF1A56DB), Color(0xFF2563EB)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); static const shimmer = LinearGradient( colors: [Color(0xFFE2E8F0), Color(0xFFF1F5F9), Color(0xFFE2E8F0)], stops: [0.0, 0.5, 1.0], begin: Alignment(-1.0, -0.3), end: Alignment(1.0, 0.3), ); } class AppAnimations { static const defaultDuration = Duration(milliseconds: 300); static const slowDuration = Duration(milliseconds: 500); static const fastDuration = Duration(milliseconds: 150); static Widget fadeSlide(Widget child, {int delay = 0}) { return child .animate() .fadeIn(duration: defaultDuration, delay: Duration(milliseconds: delay)) .slideY(begin: 0.2, end: 0, duration: defaultDuration, delay: Duration(milliseconds: delay)); } static Widget scaleIn(Widget child, {int delay = 0}) { return child .animate() .scale(begin: const Offset(0.9, 0.9), duration: defaultDuration, delay: Duration(milliseconds: delay)) .fadeIn(duration: defaultDuration, delay: Duration(milliseconds: delay)); } }