proxybuy-flutter/lib/utils/theme_manager.dart

266 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();
}
}