import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Color scheme options for the app enum AppColorScheme { blue, green, purple, orange } /// Provider for managing app theme and appearance settings /// /// Handles theme mode (light/dark/system), color scheme selection, /// and persistence of user preferences /// /// Usage example: /// ```dart /// final themeProvider = Provider.of(context); /// /// // Change theme mode /// themeProvider.setThemeMode(ThemeMode.dark); /// /// // Change color scheme /// themeProvider.setColorScheme(AppColorScheme.green); /// /// // Get current theme data /// final lightTheme = themeProvider.lightTheme; /// final darkTheme = themeProvider.darkTheme; /// ``` class ThemeProvider extends ChangeNotifier { static const String _themeModeKey = 'theme_mode'; static const String _colorSchemeKey = 'color_scheme'; ThemeMode _themeMode = ThemeMode.system; AppColorScheme _colorScheme = AppColorScheme.blue; ThemeProvider() { _loadPreferences(); } /// Get current theme mode ThemeMode get themeMode => _themeMode; /// Get current color scheme AppColorScheme get colorScheme => _colorScheme; /// Get light theme data ThemeData get lightTheme => _buildLightTheme(); /// Get dark theme data ThemeData get darkTheme => _buildDarkTheme(); /// Set theme mode /// /// [mode] - The theme mode to set (light, dark, or system) /// Saves preference and notifies listeners Future setThemeMode(ThemeMode mode) async { if (_themeMode == mode) return; _themeMode = mode; notifyListeners(); await savePreferences(); } /// Set color scheme /// /// [scheme] - The color scheme to set /// Saves preference and notifies listeners Future setColorScheme(AppColorScheme scheme) async { if (_colorScheme == scheme) return; _colorScheme = scheme; notifyListeners(); await savePreferences(); } /// Save preferences to local storage Future savePreferences() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_themeModeKey, _themeMode.name); await prefs.setString(_colorSchemeKey, _colorScheme.name); } catch (e) { debugPrint('Failed to save theme preferences: $e'); } } /// Load preferences from local storage Future _loadPreferences() async { try { final prefs = await SharedPreferences.getInstance(); // Load theme mode final themeModeStr = prefs.getString(_themeModeKey); if (themeModeStr != null) { _themeMode = ThemeMode.values.firstWhere( (mode) => mode.name == themeModeStr, orElse: () => ThemeMode.system, ); } // Load color scheme final colorSchemeStr = prefs.getString(_colorSchemeKey); if (colorSchemeStr != null) { _colorScheme = AppColorScheme.values.firstWhere( (scheme) => scheme.name == colorSchemeStr, orElse: () => AppColorScheme.blue, ); } notifyListeners(); } catch (e) { debugPrint('Failed to load theme preferences: $e'); } } /// Build light theme based on current color scheme ThemeData _buildLightTheme() { final colorScheme = _getColorScheme(Brightness.light); return ThemeData( useMaterial3: true, brightness: Brightness.light, colorScheme: colorScheme, // AppBar theme appBarTheme: AppBarTheme( centerTitle: true, elevation: 0, backgroundColor: colorScheme.primary, foregroundColor: colorScheme.onPrimary, ), // Card theme cardTheme: const CardThemeData( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(12)), ), ), // Input decoration theme inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: colorScheme.surfaceContainerHighest.withValues(alpha: 0.3), 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: BorderSide(color: colorScheme.primary, width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colorScheme.error, width: 1), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colorScheme.error, width: 2), ), ), // Elevated button theme elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 2, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), // Text button theme textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ), // Floating action button theme floatingActionButtonTheme: FloatingActionButtonThemeData( elevation: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), // Progress indicator theme progressIndicatorTheme: ProgressIndicatorThemeData( color: colorScheme.primary, ), // Divider theme dividerTheme: DividerThemeData( color: colorScheme.outlineVariant, thickness: 1, ), ); } /// Build dark theme based on current color scheme ThemeData _buildDarkTheme() { final colorScheme = _getColorScheme(Brightness.dark); return ThemeData( useMaterial3: true, brightness: Brightness.dark, colorScheme: colorScheme, // AppBar theme appBarTheme: AppBarTheme( centerTitle: true, elevation: 0, backgroundColor: colorScheme.surface, foregroundColor: colorScheme.onSurface, ), // Card theme cardTheme: const CardThemeData( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(12)), ), ), // Input decoration theme inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: colorScheme.surfaceContainerHighest.withValues(alpha: 0.3), 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: BorderSide(color: colorScheme.primary, width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colorScheme.error, width: 1), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colorScheme.error, width: 2), ), ), // Elevated button theme elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 2, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), // Text button theme textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ), // Floating action button theme floatingActionButtonTheme: FloatingActionButtonThemeData( elevation: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), // Progress indicator theme progressIndicatorTheme: ProgressIndicatorThemeData( color: colorScheme.primary, ), // Divider theme dividerTheme: DividerThemeData( color: colorScheme.outlineVariant, thickness: 1, ), ); } /// Get color scheme based on brightness and selected color ColorScheme _getColorScheme(Brightness brightness) { switch (_colorScheme) { case AppColorScheme.blue: return _getBlueColorScheme(brightness); case AppColorScheme.green: return _getGreenColorScheme(brightness); case AppColorScheme.purple: return _getPurpleColorScheme(brightness); case AppColorScheme.orange: return _getOrangeColorScheme(brightness); } } /// Blue color scheme ColorScheme _getBlueColorScheme(Brightness brightness) { if (brightness == Brightness.light) { return ColorScheme.fromSeed( seedColor: Colors.blue, brightness: Brightness.light, ); } else { return ColorScheme.fromSeed( seedColor: Colors.blue, brightness: Brightness.dark, ); } } /// Green color scheme ColorScheme _getGreenColorScheme(Brightness brightness) { if (brightness == Brightness.light) { return ColorScheme.fromSeed( seedColor: Colors.green, brightness: Brightness.light, ); } else { return ColorScheme.fromSeed( seedColor: Colors.green, brightness: Brightness.dark, ); } } /// Purple color scheme ColorScheme _getPurpleColorScheme(Brightness brightness) { if (brightness == Brightness.light) { return ColorScheme.fromSeed( seedColor: Colors.purple, brightness: Brightness.light, ); } else { return ColorScheme.fromSeed( seedColor: Colors.purple, brightness: Brightness.dark, ); } } /// Orange color scheme ColorScheme _getOrangeColorScheme(Brightness brightness) { if (brightness == Brightness.light) { return ColorScheme.fromSeed( seedColor: Colors.orange, brightness: Brightness.light, ); } else { return ColorScheme.fromSeed( seedColor: Colors.orange, brightness: Brightness.dark, ); } } /// Get color scheme name for display String getColorSchemeName(AppColorScheme scheme) { switch (scheme) { case AppColorScheme.blue: return '蓝色'; case AppColorScheme.green: return '绿色'; case AppColorScheme.purple: return '紫色'; case AppColorScheme.orange: return '橙色'; } } /// Get theme mode name for display String getThemeModeName(ThemeMode mode) { switch (mode) { case ThemeMode.light: return '浅色模式'; case ThemeMode.dark: return '深色模式'; case ThemeMode.system: return '跟随系统'; } } }