migrate dynamic theme

This commit is contained in:
shenlong-tanwen
2026-05-03 21:34:42 +07:00
parent 68a718fab4
commit ca95fbb47d
8 changed files with 38 additions and 26 deletions
@@ -4,19 +4,27 @@ import 'package:immich_mobile/constants/colors.dart';
class ThemeConfig { class ThemeConfig {
final ThemeMode mode; final ThemeMode mode;
final ImmichColorPreset primaryColor; final ImmichColorPreset primaryColor;
final bool dynamicTheme;
const ThemeConfig({this.mode = .system, this.primaryColor = .indigo}); const ThemeConfig({this.mode = .system, this.primaryColor = .indigo, this.dynamicTheme = false});
ThemeConfig copyWith({ThemeMode? mode, ImmichColorPreset? primaryColor}) => ThemeConfig copyWith({ThemeMode? mode, ImmichColorPreset? primaryColor, bool? dynamicTheme}) => .new(
.new(mode: mode ?? this.mode, primaryColor: primaryColor ?? this.primaryColor); mode: mode ?? this.mode,
primaryColor: primaryColor ?? this.primaryColor,
dynamicTheme: dynamicTheme ?? this.dynamicTheme,
);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || (other is ThemeConfig && other.mode == mode && other.primaryColor == primaryColor); identical(this, other) ||
(other is ThemeConfig &&
other.mode == mode &&
other.primaryColor == primaryColor &&
other.dynamicTheme == dynamicTheme);
@override @override
int get hashCode => Object.hash(mode, primaryColor); int get hashCode => Object.hash(mode, primaryColor, dynamicTheme);
@override @override
String toString() => 'ThemeConfig(mode: $mode, primaryColor: $primaryColor)'; String toString() => 'ThemeConfig(mode: $mode, primaryColor: $primaryColor, dynamicTheme: $dynamicTheme)';
} }
@@ -17,6 +17,7 @@ enum MetadataKey<T extends Object> {
// Theme // Theme
primaryColor<ImmichColorPreset>(.appConfig, 'theme.primaryColor', .indigo, _EnumCodec(ImmichColorPreset.values)), primaryColor<ImmichColorPreset>(.appConfig, 'theme.primaryColor', .indigo, _EnumCodec(ImmichColorPreset.values)),
themeMode<ThemeMode>(.appConfig, 'theme.mode', .system, _EnumCodec(ThemeMode.values)), themeMode<ThemeMode>(.appConfig, 'theme.mode', .system, _EnumCodec(ThemeMode.values)),
dynamicTheme<bool>(.appConfig, 'dynamicTheme', false),
// Log // Log
logLevel<LogLevel>(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values)); logLevel<LogLevel>(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values));
+1 -1
View File
@@ -49,7 +49,6 @@ enum StoreKey<T> {
customHeaders<String>._(127), customHeaders<String>._(127),
// theme settings // theme settings
dynamicTheme<bool>._(129),
colorfulInterface<bool>._(130), colorfulInterface<bool>._(130),
syncAlbums<bool>._(131), syncAlbums<bool>._(131),
@@ -95,6 +94,7 @@ enum StoreKey<T> {
// Legacy keys that have been migrated to the new metadata store // Legacy keys that have been migrated to the new metadata store
legacyPrimaryColor<String>._(128), legacyPrimaryColor<String>._(128),
legacyDynamicTheme<bool>._(129),
legacyThemeMode<String>._(102), legacyThemeMode<String>._(102),
legacyLogLevel<int>._(115); legacyLogLevel<int>._(115);
@@ -101,7 +101,11 @@ extension<T extends Object> on MetadataDomain<T> {
switch (this) { switch (this) {
case .appConfig: case .appConfig:
repo._appConfig = .new( repo._appConfig = .new(
theme: .new(mode: repo._read(.themeMode), primaryColor: repo._read(.primaryColor)), theme: .new(
mode: repo._read(.themeMode),
primaryColor: repo._read(.primaryColor),
dynamicTheme: repo._read(.dynamicTheme),
),
); );
case .systemConfig: case .systemConfig:
repo._systemConfig = .new(logLevel: repo._read(.logLevel)); repo._systemConfig = .new(logLevel: repo._read(.logLevel));
+3 -3
View File
@@ -14,9 +14,9 @@ final immichThemePresetProvider = StateProvider<ImmichColorPreset>(
(ref) => ref.watch(appConfigProvider.select((config) => config.theme.primaryColor)), (ref) => ref.watch(appConfigProvider.select((config) => config.theme.primaryColor)),
); );
final dynamicThemeSettingProvider = StateProvider<bool>((ref) { final dynamicThemeSettingProvider = StateProvider<bool>(
return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.dynamicTheme); (ref) => ref.watch(appConfigProvider.select((config) => config.theme.dynamicTheme)),
}); );
final colorfulInterfaceSettingProvider = StateProvider<bool>((ref) { final colorfulInterfaceSettingProvider = StateProvider<bool>((ref) {
return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.colorfulInterface); return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.colorfulInterface);
@@ -4,7 +4,6 @@ import 'package:immich_mobile/entities/store.entity.dart';
enum AppSettingsEnum<T> { enum AppSettingsEnum<T> {
loadPreview<bool>(StoreKey.loadPreview, "loadPreview", true), loadPreview<bool>(StoreKey.loadPreview, "loadPreview", true),
loadOriginal<bool>(StoreKey.loadOriginal, "loadOriginal", false), loadOriginal<bool>(StoreKey.loadOriginal, "loadOriginal", false),
dynamicTheme<bool>(StoreKey.dynamicTheme, "dynamicTheme", false),
colorfulInterface<bool>(StoreKey.colorfulInterface, "colorfulInterface", true), colorfulInterface<bool>(StoreKey.colorfulInterface, "colorfulInterface", true),
tilesPerRow<int>(StoreKey.tilesPerRow, "tilesPerRow", 4), tilesPerRow<int>(StoreKey.tilesPerRow, "tilesPerRow", 4),
dynamicLayout<bool>(StoreKey.dynamicLayout, "dynamicLayout", false), dynamicLayout<bool>(StoreKey.dynamicLayout, "dynamicLayout", false),
+7
View File
@@ -65,6 +65,13 @@ Future<void> _migrateTo26(Drift drift) async {
migrated.add(StoreKey.legacyPrimaryColor.id); migrated.add(StoreKey.legacyPrimaryColor.id);
} }
final dynamicTheme = await _readLegacyStoreInt(drift, StoreKey.legacyDynamicTheme.id);
if (dynamicTheme != null) {
final dynamicThemeValue = dynamicTheme != 0;
await repo.write(MetadataKey.dynamicTheme, dynamicThemeValue);
migrated.add(StoreKey.legacyDynamicTheme.id);
}
await _deleteLegacyStoreRows(drift, migrated); await _deleteLegacyStoreRows(drift, migrated);
} }
@@ -7,10 +7,8 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/theme.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/color_scheme.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
class PrimaryColorSetting extends HookConsumerWidget { class PrimaryColorSetting extends HookConsumerWidget {
const PrimaryColorSetting({super.key}); const PrimaryColorSetting({super.key});
@@ -18,9 +16,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final themeProvider = ref.read(immichThemeProvider); final themeProvider = ref.read(immichThemeProvider);
final themeConfig = ref.watch(appConfigProvider.select((config) => config.theme));
final currentPreset = ref.watch(appConfigProvider.select((config) => config.theme.primaryColor));
final systemPrimaryColorSetting = useAppSettingsState(AppSettingsEnum.dynamicTheme);
const tileSize = 55.0; const tileSize = 55.0;
@@ -31,9 +27,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
} }
onUseSystemColorChange(bool newValue) { onUseSystemColorChange(bool newValue) {
systemPrimaryColorSetting.value = newValue; ref.read(metadataProvider).write(MetadataKey.dynamicTheme, newValue);
ref.watch(dynamicThemeSettingProvider.notifier).state = newValue;
ref.invalidate(immichThemeProvider);
popBottomSheet(); popBottomSheet();
} }
@@ -41,11 +35,10 @@ class PrimaryColorSetting extends HookConsumerWidget {
ref.read(metadataProvider).write(MetadataKey.primaryColor, colorPreset); ref.read(metadataProvider).write(MetadataKey.primaryColor, colorPreset);
//turn off system color setting //turn off system color setting
if (systemPrimaryColorSetting.value) { if (themeConfig.dynamicTheme) {
onUseSystemColorChange(false); ref.read(metadataProvider).write(MetadataKey.dynamicTheme, false);
} else {
popBottomSheet();
} }
popBottomSheet();
} }
buildPrimaryColorTile({ buildPrimaryColorTile({
@@ -115,7 +108,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
'theme_setting_system_primary_color_title'.tr(), 'theme_setting_system_primary_color_title'.tr(),
style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5), style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5),
), ),
value: systemPrimaryColorSetting.value, value: themeConfig.dynamicTheme,
onChanged: onUseSystemColorChange, onChanged: onUseSystemColorChange,
), ),
), ),
@@ -133,7 +126,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
topColor: theme.light.primary, topColor: theme.light.primary,
bottomColor: theme.dark.primary, bottomColor: theme.dark.primary,
tileSize: tileSize, tileSize: tileSize,
showSelector: currentPreset == preset && !systemPrimaryColorSetting.value, showSelector: themeConfig.primaryColor == preset && !themeConfig.dynamicTheme,
), ),
); );
}).toList(), }).toList(),