Files
hash-wallet/lib/themes/core/theme_store.dart
T
c746b85a4b UI updates (#2368)
* Add Black theme

* Fix black theme

* Add WixMadefor Font

* feat: Enhance theme setup

This change:
- Adds support for custom themes
- Updates theme retrieval logic to ensure the right theme is used based on user preferences(especially for custom themes).

* Add black custom colors

* Add custom accent colors

* feat: Restructure custom colors handling for themes

* feat: Integrate new custom colors structure across various components - WIP

* feat: Integrate new custom colors structure across various components, black theme now ready for tests

* feat: Add new accent color themes and update theme selection UI

- Enhanced theme selection UI in display settings to support accent color choices.
- Updated theme handling logic to accommodate new accent color variants.

* Minor updates to display_settings_page.dart

* Update settings_theme_choice.dart

* Revert accidental change in display_settings_page.dart

* Revert WixMadeFor font, fix SVG colors for CakeImageWidget, and test some color changes

* Update Monero Orange

* fix: Remove unused custom colors

* Test outline

* Test spacing and colors

* Add padding

* Remove outline

* Add other accent colors

* Update display_settings_view_model.dart

* Update names and fix switch case

* Add outline and update dark_theme.dart colors

* Add gradient to option_tile.dart

* Update icons

* Update some colors

* Switch option_tile.dart to TextButton

* Add updated icons

* Fix icon resolution

* Cleanup old icons + Update pubspec_base.yaml

* Remove unused fonts

* Update more icons

* Update image paths

* Remove more icons + update some component colors

* Add back DAI icon

* Fix errors

* feat: Implement OLED mode for BlackTheme

* feat: Add OLED mode for BlackTheme and localizations

* feat: Implement OLED mode for Black Theme

* refactor: Remove unused theme parameters and set up easy access to toggleKnob custom colors

* fix: Cleanup UI and overflows

* feat: Persist black theme selection if OS theme mode while app is in system mode

* fix: OS theme change when app is in background restrict pin screen from proceed

* Fix theme picker sizing

* Update lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* refactor: Enhance theme access by introducing ThemeX extension for easier retrieval of current theme and custom colors

* refactor: Remove currentTheme parameter from GradientBackground widget and adjust across affected widgets

* refactor: Remove currentTheme parameter from StandardSwitch and SettingsSwitcherCell, using ThemeX extension for theme access

* refactor: Remove currentTheme parameter from various widgets dependent on SettingsSwitcherCell and StandardxSwitcher, using ThemeX extension for theme access

* refactor: Remove currentTheme in bottomsheets and related widgets, switching to theme extension

* refactor: Remove constructor theme access in OptionTile and related widgets, switching to theme extension

* refactor: Switch bottomsheet related widgets to use theme extension instead of constructor access

* refactor: Migrate currentTheme access from constructor based to extension base

* refactor: Switch theme access from constructor base to extension base [skip ci]

* refactor: Migrate more widgets to use extension based theme access and clean up code

* refactor: Update theme access across multiple widgets to use the new theme extension

* refactor: Clean up theme access in various widgets, moving to extension-based theme usage

* refactor: Make theme picker preview responsive

* fix: Persist saved black theme accent when app is put in background and brought back

* remove unused variables [skip ci]

---------

Co-authored-by: Blazebrain <davidadegoke16@gmail.com>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
2025-10-10 16:00:39 +03:00

289 lines
8.3 KiB
Dart

import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/themes/utils/theme_list.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'material_base_theme.dart';
import 'package:cake_wallet/themes/theme_classes/black_theme.dart';
part 'theme_store.g.dart';
/// MobX store for managing theme state
class ThemeStore = ThemeStoreBase with _$ThemeStore;
abstract class ThemeStoreBase with Store {
@observable
MaterialThemeBase _currentTheme = ThemeList.lightTheme;
@observable
ThemeMode _themeMode = ThemeMode.system;
@observable
bool _isOled = false;
@computed
MaterialThemeBase get currentTheme => _currentTheme;
@computed
ThemeMode get themeMode => _themeMode;
@computed
bool get isDarkMode => _currentTheme.isDark;
@computed
bool get isOled => _isOled;
@computed
bool get hasCustomTheme => sharedPreferences.getInt(PreferencesKey.currentTheme) != null;
@computed
MaterialThemeBase? get savedCustomTheme {
final raw = sharedPreferences.getInt(PreferencesKey.currentTheme);
return raw != null ? ThemeList.deserialize(raw: raw) : null;
}
@computed
MaterialThemeBase? get savedDarkTheme {
try {
final raw = sharedPreferences.getInt(PreferencesKey.savedDarkTheme);
return raw != null ? ThemeList.deserialize(raw: raw) : null;
} catch (e) {
return null;
}
}
@computed
MaterialThemeBase? get savedLightTheme {
try {
final raw = sharedPreferences.getInt(PreferencesKey.savedLightTheme);
return raw != null ? ThemeList.deserialize(raw: raw) : null;
} catch (e) {
return null;
}
}
late SharedPreferences sharedPreferences;
@action
Future<void> setTheme(MaterialThemeBase theme) async {
if (_currentTheme == theme) return;
_currentTheme = theme;
await sharedPreferences.setInt(PreferencesKey.currentTheme, theme.raw);
_isOled = theme is BlackTheme ? theme.isOled : false;
await sharedPreferences.setBool(PreferencesKey.blackThemeOled, _isOled);
// Save preferred themes for system mode
if (theme.isDark) {
await sharedPreferences.setInt(PreferencesKey.savedDarkTheme, theme.raw);
} else {
await sharedPreferences.setInt(PreferencesKey.savedLightTheme, theme.raw);
}
}
@action
Future<void> setThemeMode(ThemeMode mode) async {
if (_themeMode == mode) return;
_themeMode = mode;
await _saveThemeModeToPrefs(mode);
if (mode == ThemeMode.system) {
// We'll always use getThemeFromSystem() for system mode to respect saved themes
await setTheme(getThemeFromSystem());
return;
}
if (!hasCustomTheme) return;
final savedTheme = savedCustomTheme;
if (savedTheme == null) return;
if (_isThemeCompatibleWithMode(savedTheme, mode)) {
await setTheme(savedTheme);
}
}
@action
Future<void> setOledEnabled(bool value) async {
if (_isOled == value) return;
_isOled = value;
await sharedPreferences.setBool(PreferencesKey.blackThemeOled, value);
if (_currentTheme is BlackTheme) {
final current = _currentTheme as BlackTheme;
await setTheme(BlackTheme(current.accentColor, isOled: value));
}
}
Future<void> _saveThemeModeToPrefs(ThemeMode mode) async {
await sharedPreferences.setString(PreferencesKey.themeMode, mode.toString());
}
/// Loads the saved theme preferences
Future<void> loadThemePreferences() async {
sharedPreferences = await SharedPreferences.getInstance();
await _loadThemeMode();
await _loadAndSetTheme();
_setupThemeReaction();
}
/// Loads the saved theme mode from SharedPreferences
Future<void> _loadThemeMode() async {
final savedThemeMode = sharedPreferences.getString(PreferencesKey.themeMode);
if (savedThemeMode != null) {
_themeMode = ThemeMode.values.firstWhere(
(mode) => mode.toString() == savedThemeMode,
orElse: () => ThemeMode.system,
);
}
}
/// Loads and sets the appropriate theme based on platform and installation status
Future<void> _loadAndSetTheme() async {
final bool isNewInstall = sharedPreferences.getBool(PreferencesKey.isNewInstall) ?? true;
bool shouldUseMobileTheme =
responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile;
if (shouldUseMobileTheme) {
await _handleMobileTheme(isNewInstall);
} else {
await _handleNonMobileTheme();
}
}
/// Handles theme loading for non-mobile platforms
Future<void> _handleNonMobileTheme() async {
await setTheme(ThemeList.darkTheme);
await setThemeMode(ThemeMode.dark);
}
/// Handles theme loading for mobile platforms
Future<void> _handleMobileTheme(bool isNewInstall) async {
if (isNewInstall) {
await _setSystemTheme(isNewInstall: isNewInstall);
} else {
await loadSavedTheme();
}
}
/// Loads the saved theme from SharedPreferences
Future<void> loadSavedTheme({bool isFromBackup = false}) async {
try {
_isOled = sharedPreferences.getBool(PreferencesKey.blackThemeOled) ?? false;
final theme = savedCustomTheme;
if (!hasCustomTheme || theme == null) {
await _setSystemTheme();
return;
}
if (theme is BlackTheme) {
final adjusted = BlackTheme(theme.accentColor, isOled: _isOled);
if (_currentTheme != adjusted) {
await setTheme(adjusted);
}
} else if (_currentTheme != theme) {
await setTheme(theme);
}
final newThemeMode = _getThemeModeOnStartUp(theme, isFromBackup);
if (newThemeMode == ThemeMode.system) {
await _setSystemTheme();
return;
}
if (_themeMode != newThemeMode) {
await setThemeMode(newThemeMode);
}
} catch (e) {
await _setSystemTheme();
}
}
Future<void> _setSystemTheme({bool isNewInstall = false}) async {
if (!isNewInstall && hasCustomTheme) return;
final systemTheme = getThemeFromSystem();
if (_currentTheme != systemTheme) {
await setTheme(systemTheme);
}
if (isNewInstall) {
await _saveThemeModeToPrefs(ThemeMode.system);
if (_themeMode != ThemeMode.system) {
await setThemeMode(ThemeMode.system);
}
}
}
ThemeMode _getThemeModeOnStartUp(MaterialThemeBase theme, bool isFromBackup) {
if (isFromBackup || _themeMode != ThemeMode.system) {
return theme.isDark ? ThemeMode.dark : ThemeMode.light;
}
return ThemeMode.system;
}
void _setupThemeReaction() {
reaction(
(_) => currentTheme,
(MaterialThemeBase theme) => sharedPreferences.setInt(PreferencesKey.currentTheme, theme.raw),
);
}
MaterialThemeBase getThemeFromSystem() {
final systemBrightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
if (systemBrightness == Brightness.dark) {
// Prefer saved dark theme when available
try {
final savedDark = savedDarkTheme;
if (savedDark != null) {
if (savedDark is BlackTheme) {
// We'll use the OLED flag from the saved theme itself, not from separate preference
return BlackTheme(savedDark.accentColor, isOled: savedDark.isOled);
}
return savedDark;
}
} catch (_) {}
// If no saved dark theme, check if current theme is BlackTheme with accent colors
// and preserve it when switching to dark mode
if (_currentTheme is BlackTheme) {
final currentBlackTheme = _currentTheme as BlackTheme;
return BlackTheme(currentBlackTheme.accentColor, isOled: currentBlackTheme.isOled);
}
return ThemeList.darkTheme;
}
// Light system preference: prefer saved light theme when available
try {
final savedLight = savedLightTheme;
if (savedLight != null) return savedLight;
} catch (_) {}
return ThemeList.lightTheme;
}
/// Checks if a theme is compatible with a theme mode
bool _isThemeCompatibleWithMode(MaterialThemeBase theme, ThemeMode mode) {
switch (mode) {
case ThemeMode.light:
return !theme.isDark;
case ThemeMode.dark:
return theme.isDark;
case ThemeMode.system:
return true; // All themes are compatible with system mode
}
}
}