diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 55e74ab9d..5b1a582f5 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -6,6 +6,7 @@ import 'providers/auth_provider.dart'; import 'providers/accounts_provider.dart'; import 'providers/transactions_provider.dart'; import 'providers/chat_provider.dart'; +import 'providers/theme_provider.dart'; import 'screens/backend_config_screen.dart'; import 'screens/login_screen.dart'; import 'screens/main_navigation_screen.dart'; @@ -35,6 +36,7 @@ class SureApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => ConnectivityService()), ChangeNotifierProvider(create: (_) => AuthProvider()), ChangeNotifierProvider(create: (_) => ChatProvider()), + ChangeNotifierProvider(create: (_) => ThemeProvider()), ChangeNotifierProxyProvider( create: (_) => AccountsProvider(), update: (_, connectivityService, accountsProvider) { @@ -62,7 +64,8 @@ class SureApp extends StatelessWidget { }, ), ], - child: MaterialApp( + child: Consumer( + builder: (context, themeProvider, _) => MaterialApp( title: 'Sure Finances', debugShowCheckedModeBanner: false, theme: ThemeData( @@ -139,14 +142,14 @@ class SureApp extends StatelessWidget { ), ), ), - themeMode: ThemeMode.system, + themeMode: themeProvider.themeMode, routes: { '/config': (context) => const BackendConfigScreen(), '/login': (context) => const LoginScreen(), '/home': (context) => const MainNavigationScreen(), }, home: const AppWrapper(), - ), + )), ); } } diff --git a/mobile/lib/providers/theme_provider.dart b/mobile/lib/providers/theme_provider.dart new file mode 100644 index 000000000..51920a45a --- /dev/null +++ b/mobile/lib/providers/theme_provider.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import '../services/preferences_service.dart'; + +class ThemeProvider extends ChangeNotifier { + ThemeMode _themeMode = ThemeMode.system; + + ThemeMode get themeMode => _themeMode; + + ThemeProvider() { + _loadThemeMode(); + } + + Future _loadThemeMode() async { + try { + final mode = await PreferencesService.instance.getThemeMode(); + _themeMode = _fromString(mode); + } catch (_) { + _themeMode = ThemeMode.system; + } + notifyListeners(); + } + + Future setThemeMode(ThemeMode mode) async { + _themeMode = mode; + notifyListeners(); + await PreferencesService.instance.setThemeMode(_toString(mode)); + } + + static ThemeMode _fromString(String mode) { + switch (mode) { + case 'light': + return ThemeMode.light; + case 'dark': + return ThemeMode.dark; + default: + return ThemeMode.system; + } + } + + static String _toString(ThemeMode mode) { + switch (mode) { + case ThemeMode.light: + return 'light'; + case ThemeMode.dark: + return 'dark'; + case ThemeMode.system: + return 'system'; + } + } +} diff --git a/mobile/lib/screens/settings_screen.dart b/mobile/lib/screens/settings_screen.dart index 7a724cf1b..867e22c7b 100644 --- a/mobile/lib/screens/settings_screen.dart +++ b/mobile/lib/screens/settings_screen.dart @@ -3,6 +3,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import '../providers/auth_provider.dart'; +import '../providers/theme_provider.dart'; import '../services/offline_storage_service.dart'; import '../services/log_service.dart'; import '../services/preferences_service.dart'; @@ -399,6 +400,37 @@ class _SettingsScreenState extends State { }, ), + Consumer( + builder: (context, themeProvider, _) { + return ListTile( + leading: const Icon(Icons.brightness_6_outlined), + title: const Text('Theme'), + trailing: SegmentedButton( + segments: const [ + ButtonSegment( + value: ThemeMode.light, + icon: Icon(Icons.light_mode, size: 18), + tooltip: 'Light', + ), + ButtonSegment( + value: ThemeMode.system, + icon: Icon(Icons.brightness_auto, size: 18), + tooltip: 'System', + ), + ButtonSegment( + value: ThemeMode.dark, + icon: Icon(Icons.dark_mode, size: 18), + tooltip: 'Dark', + ), + ], + selected: {themeProvider.themeMode}, + onSelectionChanged: (modes) => themeProvider.setThemeMode(modes.first), + showSelectedIcon: false, + ), + ); + }, + ), + const Divider(), // Data Management Section diff --git a/mobile/lib/services/preferences_service.dart b/mobile/lib/services/preferences_service.dart index 15558385e..35b671e71 100644 --- a/mobile/lib/services/preferences_service.dart +++ b/mobile/lib/services/preferences_service.dart @@ -2,6 +2,7 @@ import 'package:shared_preferences/shared_preferences.dart'; class PreferencesService { static const _groupByTypeKey = 'dashboard_group_by_type'; + static const _themeModeKey = 'theme_mode'; static PreferencesService? _instance; SharedPreferences? _prefs; @@ -27,4 +28,15 @@ class PreferencesService { final prefs = await _preferences; await prefs.setBool(_groupByTypeKey, value); } + + /// Returns 'light', 'dark', or 'system' (default). + Future getThemeMode() async { + final prefs = await _preferences; + return prefs.getString(_themeModeKey) ?? 'system'; + } + + Future setThemeMode(String mode) async { + final prefs = await _preferences; + await prefs.setString(_themeModeKey, mode); + } } diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 1b33e18f2..d69aac7fa 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -1,7 +1,7 @@ name: sure_mobile description: A mobile app for Sure personal finance management publish_to: 'none' -version: 0.6.9+1 +version: 0.6.9+9 environment: sdk: '>=3.0.0 <4.0.0'