262 lines
9.7 KiB
Dart
262 lines
9.7 KiB
Dart
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<void> _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<void> toggleTheme() async {
|
|
_isDarkMode = !_isDarkMode;
|
|
_hasManualOverride = true;
|
|
AppColors.setDarkMode(_isDarkMode);
|
|
_updateSystemUIOverlay();
|
|
await _prefs.saveBool(SharedPreferencesKey.isDarkMode, _isDarkMode);
|
|
await _prefs.saveBool(SharedPreferencesKey.hasManualThemeOverride, _hasManualOverride);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> 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<void> resetToSystemTheme() async {
|
|
_hasManualOverride = false;
|
|
_isDarkMode = _getSystemTheme();
|
|
AppColors.setDarkMode(_isDarkMode);
|
|
_updateSystemUIOverlay();
|
|
await _prefs.saveBool(SharedPreferencesKey.hasManualThemeOverride, _hasManualOverride);
|
|
await _prefs.saveBool(SharedPreferencesKey.isDarkMode, _isDarkMode);
|
|
notifyListeners();
|
|
}
|
|
} |