Mobile: Add theme selection (light/dark/system) to settings (#1213)

* Feature: Add Theme selection in Settings page

* Fix: Theme provider exception handling.

* feat(mobile): Show theme selection option in settings screen.

* BuildID version 9

---------

Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
Tristan Katana
2026-03-27 00:47:31 +03:00
committed by GitHub
parent f42b593b9e
commit b1fd8bbc99
5 changed files with 101 additions and 4 deletions

View File

@@ -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<ConnectivityService, AccountsProvider>(
create: (_) => AccountsProvider(),
update: (_, connectivityService, accountsProvider) {
@@ -62,7 +64,8 @@ class SureApp extends StatelessWidget {
},
),
],
child: MaterialApp(
child: Consumer<ThemeProvider>(
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(),
),
)),
);
}
}

View File

@@ -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<void> _loadThemeMode() async {
try {
final mode = await PreferencesService.instance.getThemeMode();
_themeMode = _fromString(mode);
} catch (_) {
_themeMode = ThemeMode.system;
}
notifyListeners();
}
Future<void> 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';
}
}
}

View File

@@ -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<SettingsScreen> {
},
),
Consumer<ThemeProvider>(
builder: (context, themeProvider, _) {
return ListTile(
leading: const Icon(Icons.brightness_6_outlined),
title: const Text('Theme'),
trailing: SegmentedButton<ThemeMode>(
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

View File

@@ -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<String> getThemeMode() async {
final prefs = await _preferences;
return prefs.getString(_themeModeKey) ?? 'system';
}
Future<void> setThemeMode(String mode) async {
final prefs = await _preferences;
await prefs.setString(_themeModeKey, mode);
}
}

View File

@@ -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'