import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:lba/res/colors.dart'; import 'package:lba/utils/sharedPreferencesKey.dart'; import 'package:lba/utils/sharedPreferencesManger.dart'; class ThemeManager extends ChangeNotifier with WidgetsBindingObserver { late SharedPreferencesManager _prefs; bool _isDarkMode = false; bool _isThemeTransitioning = false; bool _hasManualOverride = false; ThemeManager() { _prefs = SharedPreferencesManager(); _loadTheme(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } bool get isDarkMode => _isDarkMode; bool get isThemeTransitioning => _isThemeTransitioning; ThemeData get lightTheme => ThemeData( fontFamily: 'Roboto', brightness: Brightness.light, scaffoldBackgroundColor: LightAppColors.scaffoldBackground, primaryColor: LightAppColors.inputBorder, buttonTheme: ButtonThemeData( buttonColor: LightAppColors.inputBorder, ), appBarTheme: AppBarTheme( backgroundColor: LightAppColors.surface, foregroundColor: LightAppColors.textPrimary, surfaceTintColor: LightAppColors.surface, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), titleTextStyle: TextStyle( color: LightAppColors.textPrimary, fontSize: 20, fontWeight: FontWeight.w600, ), ), bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: LightAppColors.surface, selectedItemColor: LightAppColors.primary, unselectedItemColor: LightAppColors.textSecondary, ), dialogTheme: DialogTheme( backgroundColor: LightAppColors.surface, titleTextStyle: TextStyle(color: LightAppColors.textPrimary), contentTextStyle: TextStyle(color: LightAppColors.textSecondary), ), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: MenuStyle( backgroundColor: MaterialStatePropertyAll(LightAppColors.surface), ), ), inputDecorationTheme: InputDecorationTheme( labelStyle: TextStyle(color: LightAppColors.textPrimary), hintStyle: TextStyle(color: LightAppColors.hint), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: LightAppColors.divider), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: LightAppColors.inputBorder, width: 2, ), ), ), pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: CupertinoPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), }, ), textTheme: TextTheme( bodyLarge: TextStyle(color: LightAppColors.textPrimary), bodyMedium: TextStyle(color: LightAppColors.textPrimary), bodySmall: TextStyle(color: LightAppColors.textSecondary), headlineLarge: TextStyle(color: LightAppColors.textPrimary), headlineMedium: TextStyle(color: LightAppColors.textPrimary), headlineSmall: TextStyle(color: LightAppColors.textPrimary), titleLarge: TextStyle(color: LightAppColors.textPrimary), titleMedium: TextStyle(color: LightAppColors.textPrimary), titleSmall: TextStyle(color: LightAppColors.textPrimary), ), ); ThemeData get darkTheme => ThemeData( fontFamily: 'Roboto', brightness: Brightness.dark, scaffoldBackgroundColor: DarkAppColors.scaffoldBackground, primaryColor: DarkAppColors.inputBorder, buttonTheme: ButtonThemeData( buttonColor: DarkAppColors.inputBorder, ), appBarTheme: AppBarTheme( backgroundColor: DarkAppColors.surface, foregroundColor: DarkAppColors.textPrimary, surfaceTintColor: DarkAppColors.surface, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.light, statusBarBrightness: Brightness.dark, ), titleTextStyle: TextStyle( color: DarkAppColors.textPrimary, fontSize: 20, fontWeight: FontWeight.w600, ), ), bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: DarkAppColors.surface, selectedItemColor: DarkAppColors.primary, unselectedItemColor: DarkAppColors.textSecondary, ), dialogTheme: DialogTheme( backgroundColor: DarkAppColors.surface, titleTextStyle: TextStyle(color: DarkAppColors.textPrimary), contentTextStyle: TextStyle(color: DarkAppColors.textSecondary), ), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: MenuStyle( backgroundColor: MaterialStatePropertyAll(DarkAppColors.surface), ), ), inputDecorationTheme: InputDecorationTheme( labelStyle: TextStyle(color: DarkAppColors.textPrimary), hintStyle: TextStyle(color: DarkAppColors.hint), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: DarkAppColors.divider), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: DarkAppColors.inputBorder, width: 2, ), ), ), pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: CupertinoPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), }, ), textTheme: TextTheme( bodyLarge: TextStyle(color: DarkAppColors.textPrimary), bodyMedium: TextStyle(color: DarkAppColors.textPrimary), bodySmall: TextStyle(color: DarkAppColors.textSecondary), headlineLarge: TextStyle(color: DarkAppColors.textPrimary), headlineMedium: TextStyle(color: DarkAppColors.textPrimary), headlineSmall: TextStyle(color: DarkAppColors.textPrimary), titleLarge: TextStyle(color: DarkAppColors.textPrimary), titleMedium: TextStyle(color: DarkAppColors.textPrimary), titleSmall: TextStyle(color: DarkAppColors.textSecondary), ), ); ThemeData get currentTheme => _isDarkMode ? darkTheme : lightTheme; /// Returns the appropriate ThemeMode based on user preferences ThemeMode getThemeMode() { if (_hasManualOverride) { return _isDarkMode ? ThemeMode.dark : ThemeMode.light; } else { return ThemeMode.system; } } @override void didChangePlatformBrightness() { super.didChangePlatformBrightness(); // Only respond to system theme changes if user hasn't manually overridden if (!_hasManualOverride) { bool systemIsDark = _getSystemTheme(); if (_isDarkMode != systemIsDark) { _isDarkMode = systemIsDark; AppColors.setDarkMode(_isDarkMode); _updateSystemUIOverlay(); notifyListeners(); } } } Future _loadTheme() async { await _prefs.init(); _hasManualOverride = _prefs.getBool(SharedPreferencesKey.hasManualThemeOverride) ?? false; if (_hasManualOverride) { _isDarkMode = _prefs.getBool(SharedPreferencesKey.isDarkMode) ?? false; } else { _isDarkMode = _getSystemTheme(); } AppColors.setDarkMode(_isDarkMode); _updateSystemUIOverlay(); notifyListeners(); } bool _getSystemTheme() { return WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; } void _updateSystemUIOverlay() { SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: _isDarkMode ? Brightness.light : Brightness.dark, statusBarBrightness: _isDarkMode ? Brightness.dark : Brightness.light, systemNavigationBarColor: _isDarkMode ? DarkAppColors.scaffoldBackground : LightAppColors.scaffoldBackground, systemNavigationBarIconBrightness: _isDarkMode ? Brightness.light : Brightness.dark, systemNavigationBarDividerColor: _isDarkMode ? DarkAppColors.divider : LightAppColors.divider, ), ); } Future toggleTheme() async { _isDarkMode = !_isDarkMode; _hasManualOverride = true; AppColors.setDarkMode(_isDarkMode); _updateSystemUIOverlay(); await _prefs.saveBool(SharedPreferencesKey.isDarkMode, _isDarkMode); await _prefs.saveBool(SharedPreferencesKey.hasManualThemeOverride, _hasManualOverride); notifyListeners(); } Future setTheme(bool isDark) async { if (_isDarkMode != isDark) { _isDarkMode = isDark; _hasManualOverride = true; AppColors.setDarkMode(_isDarkMode); _updateSystemUIOverlay(); await _prefs.saveBool(SharedPreferencesKey.isDarkMode, _isDarkMode); await _prefs.saveBool(SharedPreferencesKey.hasManualThemeOverride, _hasManualOverride); notifyListeners(); } } Future resetToSystemTheme() async { _hasManualOverride = false; _isDarkMode = _getSystemTheme(); AppColors.setDarkMode(_isDarkMode); _updateSystemUIOverlay(); await _prefs.saveBool(SharedPreferencesKey.hasManualThemeOverride, _hasManualOverride); await _prefs.saveBool(SharedPreferencesKey.isDarkMode, _isDarkMode); notifyListeners(); } }