refactor: move theme config to metadata table (#28224)

* refactor: app metadata

* refactor to per row store

* cleanup

* more test

* review changes

* more refactor

* refactor

* migrate primary color

* migrate dynamic theme

* migrate colorfulInterface

* cleanup providers

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2026-05-07 22:12:14 +07:00
committed by GitHub
parent bd58db4fcc
commit d4a97f2d25
15 changed files with 166 additions and 153 deletions
@@ -1,15 +1,14 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
class PrimaryColorSetting extends HookConsumerWidget {
const PrimaryColorSetting({super.key});
@@ -17,18 +16,10 @@ class PrimaryColorSetting extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final themeProvider = ref.read(immichThemeProvider);
final themeConfig = ref.watch(appConfigProvider.select((config) => config.theme));
final primaryColorSetting = useAppSettingsState(AppSettingsEnum.primaryColor);
final systemPrimaryColorSetting = useAppSettingsState(AppSettingsEnum.dynamicTheme);
final currentPreset = useValueNotifier(ref.read(immichThemePresetProvider));
const tileSize = 55.0;
useValueChanged(
primaryColorSetting.value,
(_, __) => currentPreset.value = ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorSetting.value),
);
void popBottomSheet() {
Future.delayed(const Duration(milliseconds: 200), () {
Navigator.pop(context);
@@ -36,23 +27,18 @@ class PrimaryColorSetting extends HookConsumerWidget {
}
onUseSystemColorChange(bool newValue) {
systemPrimaryColorSetting.value = newValue;
ref.watch(dynamicThemeSettingProvider.notifier).state = newValue;
ref.invalidate(immichThemeProvider);
ref.read(metadataProvider).write(MetadataKey.dynamicTheme, newValue);
popBottomSheet();
}
onPrimaryColorChange(ImmichColorPreset colorPreset) {
primaryColorSetting.value = colorPreset.name;
ref.watch(immichThemePresetProvider.notifier).state = colorPreset;
ref.invalidate(immichThemeProvider);
ref.read(metadataProvider).write(MetadataKey.primaryColor, colorPreset);
//turn off system color setting
if (systemPrimaryColorSetting.value) {
onUseSystemColorChange(false);
} else {
popBottomSheet();
if (themeConfig.dynamicTheme) {
ref.read(metadataProvider).write(MetadataKey.dynamicTheme, false);
}
popBottomSheet();
}
buildPrimaryColorTile({
@@ -122,7 +108,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
'theme_setting_system_primary_color_title'.tr(),
style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5),
),
value: systemPrimaryColorSetting.value,
value: themeConfig.dynamicTheme,
onChanged: onUseSystemColorChange,
),
),
@@ -140,7 +126,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
topColor: theme.light.primary,
bottomColor: theme.dark.primary,
tileSize: tileSize,
showSelector: currentPreset.value == preset && !systemPrimaryColorSetting.value,
showSelector: themeConfig.primaryColor == preset && !themeConfig.dynamicTheme,
),
);
}).toList(),
@@ -5,9 +5,6 @@ import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@@ -17,26 +14,15 @@ class ThemeSetting extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentTheme = useState(ref.read(immichThemeModeProvider));
final currentTheme = useState(ref.read(appConfigProvider.select((config) => config.theme.mode)));
final isDarkTheme = useValueNotifier(currentTheme.value == ThemeMode.dark);
final isSystemTheme = useValueNotifier(currentTheme.value == ThemeMode.system);
final applyThemeToBackgroundSetting = useAppSettingsState(AppSettingsEnum.colorfulInterface);
final applyThemeToBackgroundProvider = useValueNotifier(ref.read(colorfulInterfaceSettingProvider));
useValueChanged(
applyThemeToBackgroundSetting.value,
(_, __) => applyThemeToBackgroundProvider.value = applyThemeToBackgroundSetting.value,
final colorfulInterface = useValueNotifier(
ref.watch(appConfigProvider.select((config) => config.theme.colorfulInterface)),
);
void onThemeChange(bool isDark) {
if (isDark) {
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.dark;
currentTheme.value = ThemeMode.dark;
} else {
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.light;
currentTheme.value = ThemeMode.light;
}
currentTheme.value = isDark ? ThemeMode.dark : ThemeMode.light;
ref.read(metadataProvider).write(MetadataKey.themeMode, currentTheme.value);
}
@@ -44,25 +30,22 @@ class ThemeSetting extends HookConsumerWidget {
if (isSystem) {
currentTheme.value = ThemeMode.system;
isSystemTheme.value = true;
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.system;
} else {
final currentSystemBrightness = context.platformBrightness;
isSystemTheme.value = false;
isDarkTheme.value = currentSystemBrightness == Brightness.dark;
if (currentSystemBrightness == Brightness.light) {
currentTheme.value = ThemeMode.light;
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.light;
} else if (currentSystemBrightness == Brightness.dark) {
currentTheme.value = ThemeMode.dark;
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.dark;
}
}
ref.read(metadataProvider).write(MetadataKey.themeMode, currentTheme.value);
}
void onSurfaceColorSettingChange(bool useColorfulInterface) {
applyThemeToBackgroundSetting.value = useColorfulInterface;
ref.watch(colorfulInterfaceSettingProvider.notifier).state = useColorfulInterface;
ref.read(metadataProvider).write(MetadataKey.colorfulInterface, useColorfulInterface);
colorfulInterface.value = useColorfulInterface;
}
return Column(
@@ -85,7 +68,7 @@ class ThemeSetting extends HookConsumerWidget {
),
const PrimaryColorSetting(),
SettingsSwitchListTile(
valueNotifier: applyThemeToBackgroundProvider,
valueNotifier: colorfulInterface,
title: "theme_setting_colorful_interface_title".t(context: context),
subtitle: 'theme_setting_colorful_interface_subtitle'.t(context: context),
onChanged: onSurfaceColorSettingChange,