- Add @persistentRiverpod annotation for providers that need keepAlive - ThemeProvider now persists for app lifetime - Refactored API clients to use @persistentRiverpod - Documented in ARCHITECTURE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
98 lines
2.8 KiB
Dart
98 lines
2.8 KiB
Dart
import 'dart:developer' as developer;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:tatlock_ui/core/auth/auth_provider.dart';
|
|
import 'package:tatlock_ui/core/providers/annotations.dart';
|
|
|
|
part 'theme_provider.g.dart';
|
|
|
|
/// User's theme preference setting.
|
|
enum ThemeSetting {
|
|
/// Follow system preference
|
|
system,
|
|
|
|
/// Always light mode
|
|
light,
|
|
|
|
/// Always dark mode
|
|
dark,
|
|
}
|
|
|
|
/// Provider for theme setting state.
|
|
///
|
|
/// Syncs with API preferences when user is authenticated. On login, the theme
|
|
/// from API preferences takes precedence over local storage.
|
|
@persistentRiverpod
|
|
class ThemeNotifier extends _$ThemeNotifier {
|
|
static const _prefsKey = 'theme_setting';
|
|
|
|
@override
|
|
ThemeSetting build() {
|
|
// Load local setting first for immediate UI
|
|
_loadSavedSetting();
|
|
|
|
// Listen for auth state changes to sync from API preferences
|
|
_syncFromAuthPreferences();
|
|
|
|
return ThemeSetting.system;
|
|
}
|
|
|
|
Future<void> _loadSavedSetting() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final value = prefs.getString(_prefsKey);
|
|
if (value != null) {
|
|
try {
|
|
state = ThemeSetting.values.byName(value);
|
|
} catch (_) {
|
|
// Invalid value, keep default
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Listen to auth state and sync theme from API preferences.
|
|
void _syncFromAuthPreferences() {
|
|
ref.listen(authProvider, (_, next) {
|
|
next.whenData((auth) {
|
|
final apiTheme = auth.preferences?.theme;
|
|
if (apiTheme != null && apiTheme.isNotEmpty) {
|
|
try {
|
|
final themeSetting = ThemeSetting.values.byName(apiTheme);
|
|
if (themeSetting != state) {
|
|
developer.log(
|
|
'Syncing theme from API: $apiTheme',
|
|
name: 'theme',
|
|
);
|
|
state = themeSetting;
|
|
// Also persist to local storage for offline use
|
|
_saveToLocalStorage(themeSetting);
|
|
}
|
|
} catch (_) {
|
|
// Invalid theme value from API, keep current
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
Future<void> _saveToLocalStorage(ThemeSetting setting) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString(_prefsKey, setting.name);
|
|
}
|
|
|
|
/// Update theme setting and persist to storage.
|
|
Future<void> setSetting(ThemeSetting setting) async {
|
|
state = setting;
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString(_prefsKey, setting.name);
|
|
}
|
|
|
|
/// Get the ThemeMode for MaterialApp.
|
|
ThemeMode get themeMode => switch (state) {
|
|
ThemeSetting.system => ThemeMode.system,
|
|
ThemeSetting.light => ThemeMode.light,
|
|
ThemeSetting.dark => ThemeMode.dark,
|
|
};
|
|
}
|