import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'colors.dart'; class AppTheme { AppTheme._(); // ─── Dark Theme (Premium SaaS) ───────────────────────────────────────────── static ThemeData get darkTheme { final darkScheme = ColorScheme.dark( brightness: Brightness.dark, primary: AppColor.accent, onPrimary: Colors.white, primaryContainer: AppColor.accentSoft, onPrimaryContainer: AppColor.accent, secondary: AppColor.glow, onSecondary: Colors.white, secondaryContainer: const Color(0x26818CF8), onSecondaryContainer: AppColor.glow, tertiary: AppColor.info, onTertiary: Colors.white, surface: AppColor.surface, onSurface: AppColor.textPrimary, onSurfaceVariant: AppColor.textSecondary, error: AppColor.danger, onError: Colors.white, errorContainer: AppColor.dangerSoft, onErrorContainer: AppColor.danger, outline: AppColor.divider, outlineVariant: const Color(0xFF3D3D42), shadow: Colors.black, scrim: Colors.black54, inverseSurface: AppColor.textPrimary, onInverseSurface: AppColor.bg, inversePrimary: AppColor.glow, ); return ThemeData( useMaterial3: true, brightness: Brightness.dark, fontFamily: GoogleFonts.tajawal().fontFamily, scaffoldBackgroundColor: AppColor.bg, colorScheme: darkScheme, dividerColor: AppColor.divider, dividerTheme: const DividerThemeData( color: AppColor.divider, thickness: 1, space: 1, ), appBarTheme: AppBarTheme( backgroundColor: AppColor.bg, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, titleTextStyle: GoogleFonts.tajawal( color: AppColor.textPrimary, fontSize: 18, fontWeight: FontWeight.bold, ), iconTheme: const IconThemeData(color: AppColor.textPrimary), ), cardTheme: CardThemeData( color: AppColor.surface, elevation: 0, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: const BorderSide(color: AppColor.divider, width: 1), ), ), dialogTheme: DialogThemeData( backgroundColor: AppColor.surface, elevation: 24, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: const BorderSide(color: AppColor.divider, width: 1), ), titleTextStyle: GoogleFonts.tajawal( color: AppColor.textPrimary, fontSize: 18, fontWeight: FontWeight.bold, ), contentTextStyle: GoogleFonts.tajawal( color: AppColor.textSecondary, fontSize: 14, ), ), bottomSheetTheme: const BottomSheetThemeData( backgroundColor: AppColor.surface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), ), snackBarTheme: SnackBarThemeData( backgroundColor: AppColor.surfaceElevated, contentTextStyle: GoogleFonts.tajawal( color: AppColor.textPrimary, fontSize: 14, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: AppColor.surfaceElevated, 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: AppColor.accent, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColor.danger, width: 1), ), labelStyle: const TextStyle(color: AppColor.textSecondary), hintStyle: const TextStyle(color: AppColor.textMuted), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: AppColor.accent, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: AppColor.textPrimary, side: const BorderSide(color: AppColor.divider), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: AppColor.accent, textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), switchTheme: SwitchThemeData( thumbColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) return AppColor.success; return AppColor.textMuted; }), trackColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) return AppColor.successSoft; return AppColor.surfaceElevated; }), ), popupMenuTheme: PopupMenuThemeData( color: AppColor.surface, elevation: 8, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: const BorderSide(color: AppColor.divider), ), ), tooltipTheme: TooltipThemeData( decoration: BoxDecoration( color: AppColor.surfaceElevated, borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColor.divider), ), textStyle: GoogleFonts.tajawal( color: AppColor.textPrimary, fontSize: 12, ), ), floatingActionButtonTheme: const FloatingActionButtonThemeData( backgroundColor: AppColor.accent, foregroundColor: Colors.white, elevation: 4, ), progressIndicatorTheme: const ProgressIndicatorThemeData( color: AppColor.accent, linearTrackColor: AppColor.surfaceElevated, ), tabBarTheme: TabBarThemeData( labelColor: AppColor.accent, unselectedLabelColor: AppColor.textSecondary, indicatorColor: AppColor.accent, dividerColor: AppColor.divider, ), chipTheme: ChipThemeData( backgroundColor: AppColor.surfaceElevated, labelStyle: GoogleFonts.tajawal(color: AppColor.textPrimary, fontSize: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: const BorderSide(color: AppColor.divider), ), ), pageTransitionsTheme: PageTransitionsTheme( builders: { TargetPlatform.windows: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.macOS: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.linux: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.fuchsia: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.android: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.iOS: const FadeUpwardsPageTransitionsBuilder(), }, ), ); } // ─── Light Theme (Premium SaaS) ──────────────────────────────────────────── static ThemeData get lightTheme { const bgLight = Color(0xFFF8F9FC); const surfaceLight = Color(0xFFFFFFFF); const surfaceElevatedLight = Color(0xFFF1F3F9); const dividerLight = Color(0xFFE2E5ED); const textPrimaryLight = Color(0xFF111827); const textSecondaryLight = Color(0xFF6B7280); const textMutedLight = Color(0xFF9CA3AF); const accentLight = Color(0xFF6366F1); const accentSoftLight = Color(0x1A6366F1); const glowLight = Color(0xFF4F46E5); const dangerLight = Color(0xFFDC2626); const dangerSoftLight = Color(0x1ADC2626); const successLight = Color(0xFF059669); const successSoftLight = Color(0x1A059669); const infoLight = Color(0xFF2563EB); final lightScheme = ColorScheme.light( brightness: Brightness.light, primary: accentLight, onPrimary: Colors.white, primaryContainer: accentSoftLight, onPrimaryContainer: accentLight, secondary: glowLight, onSecondary: Colors.white, secondaryContainer: const Color(0x1A4F46E5), onSecondaryContainer: glowLight, tertiary: infoLight, onTertiary: Colors.white, surface: surfaceLight, onSurface: textPrimaryLight, onSurfaceVariant: textSecondaryLight, error: dangerLight, onError: Colors.white, errorContainer: dangerSoftLight, onErrorContainer: dangerLight, outline: dividerLight, outlineVariant: const Color(0xFFD1D5DB), shadow: Colors.black26, scrim: Colors.black38, inverseSurface: textPrimaryLight, onInverseSurface: surfaceLight, inversePrimary: const Color(0xFFC7D2FE), ); return ThemeData( useMaterial3: true, brightness: Brightness.light, fontFamily: GoogleFonts.tajawal().fontFamily, scaffoldBackgroundColor: bgLight, colorScheme: lightScheme, dividerColor: dividerLight, dividerTheme: const DividerThemeData( color: dividerLight, thickness: 1, space: 1, ), appBarTheme: AppBarTheme( backgroundColor: bgLight, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, titleTextStyle: GoogleFonts.tajawal( color: textPrimaryLight, fontSize: 18, fontWeight: FontWeight.bold, ), iconTheme: const IconThemeData(color: textPrimaryLight), ), cardTheme: CardThemeData( color: surfaceLight, elevation: 0, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: const BorderSide(color: dividerLight, width: 1), ), ), dialogTheme: DialogThemeData( backgroundColor: surfaceLight, elevation: 24, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: const BorderSide(color: dividerLight, width: 1), ), titleTextStyle: GoogleFonts.tajawal( color: textPrimaryLight, fontSize: 18, fontWeight: FontWeight.bold, ), contentTextStyle: GoogleFonts.tajawal( color: textSecondaryLight, fontSize: 14, ), ), bottomSheetTheme: const BottomSheetThemeData( backgroundColor: surfaceLight, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), ), snackBarTheme: SnackBarThemeData( backgroundColor: surfaceElevatedLight, contentTextStyle: GoogleFonts.tajawal( color: textPrimaryLight, fontSize: 14, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: surfaceElevatedLight, 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: accentLight, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: dangerLight, width: 1), ), labelStyle: const TextStyle(color: textSecondaryLight), hintStyle: const TextStyle(color: textMutedLight), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: accentLight, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: textPrimaryLight, side: const BorderSide(color: dividerLight), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: accentLight, textStyle: GoogleFonts.tajawal( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), switchTheme: SwitchThemeData( thumbColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) return successLight; return textMutedLight; }), trackColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) return successSoftLight; return surfaceElevatedLight; }), ), popupMenuTheme: PopupMenuThemeData( color: surfaceLight, elevation: 8, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: const BorderSide(color: dividerLight), ), ), tooltipTheme: TooltipThemeData( decoration: BoxDecoration( color: surfaceElevatedLight, borderRadius: BorderRadius.circular(8), border: Border.all(color: dividerLight), ), textStyle: GoogleFonts.tajawal( color: textPrimaryLight, fontSize: 12, ), ), floatingActionButtonTheme: const FloatingActionButtonThemeData( backgroundColor: accentLight, foregroundColor: Colors.white, elevation: 4, ), progressIndicatorTheme: const ProgressIndicatorThemeData( color: accentLight, linearTrackColor: surfaceElevatedLight, ), tabBarTheme: TabBarThemeData( labelColor: accentLight, unselectedLabelColor: textSecondaryLight, indicatorColor: accentLight, dividerColor: dividerLight, ), chipTheme: ChipThemeData( backgroundColor: surfaceElevatedLight, labelStyle: GoogleFonts.tajawal(color: textPrimaryLight, fontSize: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: const BorderSide(color: dividerLight), ), ), pageTransitionsTheme: PageTransitionsTheme( builders: { TargetPlatform.windows: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.macOS: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.linux: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.fuchsia: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.android: const FadeUpwardsPageTransitionsBuilder(), TargetPlatform.iOS: const FadeUpwardsPageTransitionsBuilder(), }, ), ); } } // ─── Context Extensions for Theme Access ────────────────────────────────────── extension ThemeExtensions on BuildContext { ColorScheme get colors => Theme.of(this).colorScheme; TextTheme get textStyles => Theme.of(this).textTheme; Brightness get brightness => Theme.of(this).brightness; bool get isDark => brightness == Brightness.dark; } // ─── Semantic Colors on ColorScheme (light/dark adaptive, no BuildContext) ──── // // ColorScheme في ماتيريال لا يحتوي خانات لـ success / warning، فكانت الشاشات // تكتب قيمها الست عشرية مباشرة (0xFF10B981 و 0xFFF59E0B ...). المشكلة أن تلك // القيم هي نسخة الثيم الداكن وحدها، فتبقى كما هي في الوضع الفاتح ويظهر التصميم // غير متناسق. // // هذا الامتداد يشتق اللون من brightness الخاص بالـ ColorScheme نفسه، فلا يحتاج // BuildContext ويصلح للاستعمال داخل الدوال المساعدة التي تستقبل `cs` فقط. // للأماكن التي يتوفر فيها context استخدم AppColors.* أدناه. extension SemanticColorScheme on ColorScheme { bool get _isDark => brightness == Brightness.dark; Color get success => _isDark ? const Color(0xFF10B981) : const Color(0xFF059669); Color get successSoft => _isDark ? const Color(0x2610B981) : const Color(0x1A059669); Color get warning => _isDark ? const Color(0xFFF59E0B) : const Color(0xFFD97706); Color get warningSoft => _isDark ? const Color(0x26F59E0B) : const Color(0x1AD97706); Color get danger => _isDark ? const Color(0xFFEF4444) : const Color(0xFFDC2626); Color get dangerSoft => _isDark ? const Color(0x26EF4444) : const Color(0x1ADC2626); Color get info => _isDark ? const Color(0xFF3B82F6) : const Color(0xFF2563EB); Color get infoSoft => _isDark ? const Color(0x263B82F6) : const Color(0x1A2563EB); Color get textMuted => _isDark ? const Color(0xFF6B7280) : const Color(0xFF9CA3AF); } // ─── Semantic Color Getters (light/dark adaptive) ───────────────────────────── // These replace direct AppColor.* usage in views. They automatically adapt // based on the current theme brightness. class AppColors { AppColors._(); static Color bg(BuildContext context) => context.isDark ? const Color(0xFF0A0A0B) : const Color(0xFFF8F9FC); static Color surface(BuildContext context) => context.isDark ? const Color(0xFF161618) : const Color(0xFFFFFFFF); static Color surfaceElevated(BuildContext context) => context.isDark ? const Color(0xFF222225) : const Color(0xFFF1F3F9); static Color surfaceGlass(BuildContext context) => context.isDark ? const Color(0xCC161618) : const Color(0xCCFFFFFF); static Color accent(BuildContext context) => context.isDark ? const Color(0xFF6366F1) : const Color(0xFF6366F1); static Color accentSoft(BuildContext context) => context.isDark ? const Color(0x266366F1) : const Color(0x1A6366F1); static Color accentBorder(BuildContext context) => context.isDark ? const Color(0x4D6366F1) : const Color(0x336366F1); static Color glow(BuildContext context) => context.isDark ? const Color(0xFF818CF8) : const Color(0xFF4F46E5); static Color danger(BuildContext context) => context.isDark ? const Color(0xFFEF4444) : const Color(0xFFDC2626); static Color dangerSoft(BuildContext context) => context.isDark ? const Color(0x26EF4444) : const Color(0x1ADC2626); static Color success(BuildContext context) => context.isDark ? const Color(0xFF10B981) : const Color(0xFF059669); static Color successSoft(BuildContext context) => context.isDark ? const Color(0x2610B981) : const Color(0x1A059669); static Color warning(BuildContext context) => context.isDark ? const Color(0xFFF59E0B) : const Color(0xFFD97706); static Color warningSoft(BuildContext context) => context.isDark ? const Color(0x26F59E0B) : const Color(0x1AD97706); static Color info(BuildContext context) => context.isDark ? const Color(0xFF3B82F6) : const Color(0xFF2563EB); static Color infoSoft(BuildContext context) => context.isDark ? const Color(0x263B82F6) : const Color(0x1A2563EB); static Color textPrimary(BuildContext context) => context.isDark ? const Color(0xFFF3F4F6) : const Color(0xFF111827); static Color textSecondary(BuildContext context) => context.isDark ? const Color(0xFF9CA3AF) : const Color(0xFF6B7280); static Color textMuted(BuildContext context) => context.isDark ? const Color(0xFF6B7280) : const Color(0xFF9CA3AF); static Color divider(BuildContext context) => context.isDark ? const Color(0xFF2D2D30) : const Color(0xFFE2E5ED); static Color cardShadow(BuildContext context) => context.isDark ? const Color(0x66000000) : const Color(0x0D000000); // Gradient helpers for charts static LinearGradient accentGradient(BuildContext context) => LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ accent(context).withValues(alpha: 0.4), accent(context).withValues(alpha: 0.0), ], ); static LinearGradient successGradient(BuildContext context) => LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ success(context).withValues(alpha: 0.4), success(context).withValues(alpha: 0.0), ], ); static LinearGradient dangerGradient(BuildContext context) => LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ danger(context).withValues(alpha: 0.4), danger(context).withValues(alpha: 0.0), ], ); static LinearGradient infoGradient(BuildContext context) => LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ info(context).withValues(alpha: 0.4), info(context).withValues(alpha: 0.0), ], ); }