import 'package:flutter/material.dart'; class AppTheme { // Breakpoints pour le responsive design static const double breakpointMobileSmall = 360; // iPhone SE et petits Android static const double breakpointMobile = 600; // Smartphones standards static const double breakpointTablet = 900; // Tablettes // Multiplicateurs de taille selon l'écran static const double fontScaleExtraSmall = 0.85; // < 360px static const double fontScaleSmall = 0.9; // 360-600px static const double fontScaleMedium = 0.95; // 600-900px static const double fontScaleLarge = 1.0; // > 900px /// Calcule le multiplicateur de police selon la largeur d'écran static double getFontScaleFactor(double screenWidth) { if (screenWidth < breakpointMobileSmall) return fontScaleExtraSmall; if (screenWidth < breakpointMobile) return fontScaleSmall; if (screenWidth < breakpointTablet) return fontScaleMedium; return fontScaleLarge; } /// Retourne une taille de police responsive static double responsive(BuildContext context, double baseSize) { final width = MediaQuery.of(context).size.width; return baseSize * getFontScaleFactor(width); } /// Retourne une taille de police responsive (version courte) static double r(BuildContext context, double baseSize) => responsive(context, baseSize); // Couleurs du thème basées sur la maquette Figma static const Color primaryColor = Color(0xFF20335E); // Bleu foncé static const Color secondaryColor = Color(0xFF9DC7C8); // Bleu clair static const Color accentColor = Color(0xFF00E09D); // Vert static const Color errorColor = Color(0xFFE41B13); // Rouge static const Color warningColor = Color(0xFFF7A278); // Orange static const Color backgroundLightColor = Color(0xFFF4F5F6); // Gris très clair static const Color backgroundDarkColor = Color(0xFF111827); static const Color textLightColor = Color(0xFF000000); // Noir static const Color textDarkColor = Color(0xFFF9FAFB); // Couleurs de texte supplémentaires static const Color textSecondaryColor = Color(0xFF7F8C8D); static const Color textLightSecondaryColor = Color(0xFFBDC3C7); // Couleurs des boutons static const Color buttonSuccessColor = Color(0xFF2ECC71); static const Color buttonDangerColor = Color(0xFFE74C3C); // Couleurs des charts static const List chartColors = [ primaryColor, accentColor, errorColor, warningColor, secondaryColor, Color(0xFF9B59B6), Color(0xFF1ABC9C), ]; // Ombres static List cardShadow = [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), spreadRadius: 1, blurRadius: 10, offset: const Offset(0, 3), ), ]; static List buttonShadow = [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), spreadRadius: 1, blurRadius: 5, offset: const Offset(0, 2), ), ]; // Rayons des bordures static const double borderRadiusSmall = 4.0; static const double borderRadiusMedium = 8.0; static const double borderRadiusLarge = 12.0; static const double borderRadiusXL = 16.0; static const double borderRadiusRounded = 50.0; // Espacement static const double spacingXS = 4.0; static const double spacingS = 8.0; static const double spacingM = 16.0; static const double spacingL = 24.0; static const double spacingXL = 32.0; static const double spacingXXL = 48.0; // Thème clair static ThemeData get lightTheme { return ThemeData( useMaterial3: true, brightness: Brightness.light, fontFamily: 'Figtree', colorScheme: const ColorScheme.light( primary: primaryColor, secondary: secondaryColor, tertiary: accentColor, surface: Colors.white, onPrimary: Colors.white, onSecondary: Colors.white, onSurface: textLightColor, error: errorColor, ), scaffoldBackgroundColor: backgroundLightColor, textTheme: _getTextTheme(textLightColor), appBarTheme: const AppBarTheme( backgroundColor: primaryColor, foregroundColor: Colors.white, elevation: 0, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: primaryColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: spacingL, vertical: spacingM), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusRounded), ), textStyle: const TextStyle( fontFamily: 'Figtree', fontSize: 18, fontWeight: FontWeight.w500, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: primaryColor, side: const BorderSide(color: primaryColor), padding: const EdgeInsets.symmetric( horizontal: spacingL, vertical: spacingM), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: primaryColor, padding: const EdgeInsets.symmetric( horizontal: spacingM, vertical: spacingS), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: backgroundLightColor, border: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: BorderSide( color: textLightColor.withValues(alpha: 0.1), width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: BorderSide( color: textLightColor.withValues(alpha: 0.1), width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: const BorderSide(color: primaryColor, width: 2), ), contentPadding: const EdgeInsets.symmetric( horizontal: spacingM, vertical: spacingM), ), cardTheme: CardThemeData( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusXL), ), color: Colors.white, ), dividerTheme: const DividerThemeData( color: Color(0xFFECF0F1), thickness: 1, space: spacingM, ), ); } // Thème sombre static ThemeData get darkTheme { return ThemeData( useMaterial3: true, brightness: Brightness.dark, fontFamily: 'Figtree', colorScheme: const ColorScheme.dark( primary: primaryColor, secondary: secondaryColor, tertiary: accentColor, surface: Color(0xFF1F2937), onPrimary: Colors.white, onSecondary: Colors.white, onSurface: textDarkColor, error: errorColor, ), scaffoldBackgroundColor: backgroundDarkColor, textTheme: _getTextTheme(textDarkColor), appBarTheme: const AppBarTheme( backgroundColor: Color(0xFF1F2937), foregroundColor: Colors.white, elevation: 0, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: primaryColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: spacingL, vertical: spacingM), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusRounded), ), textStyle: const TextStyle( fontFamily: 'Figtree', fontSize: 18, fontWeight: FontWeight.w500, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: primaryColor, side: const BorderSide(color: primaryColor), padding: const EdgeInsets.symmetric( horizontal: spacingL, vertical: spacingM), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: primaryColor, padding: const EdgeInsets.symmetric( horizontal: spacingM, vertical: spacingS), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: const Color(0xFF374151), border: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: BorderSide( color: textDarkColor.withValues(alpha: 0.1), width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: BorderSide( color: textDarkColor.withValues(alpha: 0.1), width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(borderRadiusMedium), borderSide: const BorderSide(color: primaryColor, width: 2), ), contentPadding: const EdgeInsets.symmetric( horizontal: spacingM, vertical: spacingM), ), cardTheme: CardThemeData( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadiusXL), ), color: const Color(0xFF1F2937), ), dividerTheme: DividerThemeData( color: textDarkColor.withValues(alpha: 0.1), thickness: 1, space: spacingM, ), ); } // Méthode helper pour générer le TextTheme responsive static TextTheme getResponsiveTextTheme(BuildContext context, Color textColor) { final scaleFactor = getFontScaleFactor(MediaQuery.of(context).size.width); return TextTheme( // Display styles (très grandes tailles) displayLarge: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 57 * scaleFactor, // Material 3 default ), displayMedium: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 45 * scaleFactor, ), displaySmall: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 36 * scaleFactor, ), // Headline styles (titres principaux) headlineLarge: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 32 * scaleFactor, ), headlineMedium: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 28 * scaleFactor, ), headlineSmall: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 24 * scaleFactor, ), // Title styles (sous-titres) titleLarge: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 22 * scaleFactor, ), titleMedium: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 16 * scaleFactor, fontWeight: FontWeight.w500, ), titleSmall: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 14 * scaleFactor, fontWeight: FontWeight.w500, ), // Body styles (texte principal) bodyLarge: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 16 * scaleFactor, ), bodyMedium: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 14 * scaleFactor, ), bodySmall: TextStyle( fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 12 * scaleFactor, ), // Label styles (petits textes, boutons) labelLarge: TextStyle( fontFamily: 'Figtree', color: textColor, fontSize: 14 * scaleFactor, fontWeight: FontWeight.w500, ), labelMedium: TextStyle( fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 12 * scaleFactor, ), labelSmall: TextStyle( fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 11 * scaleFactor, ), ); } // Version statique pour compatibilité (utilise les tailles par défaut) static TextTheme _getTextTheme(Color textColor) { return TextTheme( displayLarge: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 57), displayMedium: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 45), displaySmall: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 36), headlineLarge: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 32), headlineMedium: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 28), headlineSmall: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 24), titleLarge: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 22), titleMedium: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 16, fontWeight: FontWeight.w500), titleSmall: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 14, fontWeight: FontWeight.w500), bodyLarge: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 16), bodyMedium: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 14), bodySmall: TextStyle(fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 12), labelLarge: TextStyle(fontFamily: 'Figtree', color: textColor, fontSize: 14, fontWeight: FontWeight.w500), labelMedium: TextStyle(fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 12), labelSmall: TextStyle(fontFamily: 'Figtree', color: textColor.withValues(alpha: 0.7), fontSize: 11), ); } /// Helper pour obtenir des espacements responsives static double getResponsiveSpacing(double screenWidth, double baseSpacing) { final scaleFactor = getFontScaleFactor(screenWidth); return baseSpacing * scaleFactor; } /// Helper court pour espacements responsives static double s(BuildContext context, double baseSpacing) { final width = MediaQuery.of(context).size.width; return getResponsiveSpacing(width, baseSpacing); } }