mirror of
https://github.com/immich-app/immich.git
synced 2024-12-28 06:31:58 +00:00
refactor(mobile): refactor theme management (#14415)
This commit is contained in:
parent
5814a1b223
commit
11f585d0ad
19 changed files with 343 additions and 322 deletions
23
mobile/lib/constants/colors.dart
Normal file
23
mobile/lib/constants/colors.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum ImmichColorPreset {
|
||||||
|
indigo,
|
||||||
|
deepPurple,
|
||||||
|
pink,
|
||||||
|
red,
|
||||||
|
orange,
|
||||||
|
yellow,
|
||||||
|
lime,
|
||||||
|
green,
|
||||||
|
cyan,
|
||||||
|
slateGray
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo;
|
||||||
|
const String defaultColorPresetName = "indigo";
|
||||||
|
|
||||||
|
const Color immichBrandColorLight = Color(0xFF4150AF);
|
||||||
|
const Color immichBrandColorDark = Color(0xFFACCBFA);
|
||||||
|
const Color whiteOpacity75 = Color.fromARGB((0.75 * 255) ~/ 1, 255, 255, 255);
|
||||||
|
const Color red400 = Color(0xFFEF5350);
|
||||||
|
const Color grey200 = Color(0xFFEEEEEE);
|
|
@ -4,23 +4,26 @@ import 'dart:io';
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
import 'package:timezone/data/latest.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
|
||||||
import 'package:immich_mobile/utils/download.dart';
|
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
|
||||||
import 'package:timezone/data/latest.dart';
|
|
||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/services/background.service.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/providers/theme.provider.dart';
|
||||||
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/routing/tab_navigation_observer.dart';
|
import 'package:immich_mobile/routing/tab_navigation_observer.dart';
|
||||||
import 'package:immich_mobile/utils/cache/widgets_binding.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
|
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
@ -30,16 +33,15 @@ import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/logger_message.entity.dart';
|
import 'package:immich_mobile/entities/logger_message.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
import 'package:immich_mobile/services/background.service.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/services/immich_logger.service.dart';
|
import 'package:immich_mobile/services/immich_logger.service.dart';
|
||||||
import 'package:immich_mobile/services/local_notification.service.dart';
|
import 'package:immich_mobile/services/local_notification.service.dart';
|
||||||
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
import 'package:immich_mobile/utils/migration.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:immich_mobile/utils/download.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:immich_mobile/utils/cache/widgets_binding.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
||||||
|
import 'package:immich_mobile/theme/theme_data.dart';
|
||||||
|
import 'package:immich_mobile/theme/dynamic_theme.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
ImmichWidgetsBinding();
|
ImmichWidgetsBinding();
|
||||||
|
@ -69,12 +71,12 @@ Future<void> initApp() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchSystemPalette();
|
await DynamicTheme.fetchSystemPalette();
|
||||||
|
|
||||||
// Initialize Immich Logger Service
|
// Initialize Immich Logger Service
|
||||||
ImmichLogger();
|
ImmichLogger();
|
||||||
|
|
||||||
var log = Logger("ImmichErrorLogger");
|
final log = Logger("ImmichErrorLogger");
|
||||||
|
|
||||||
FlutterError.onError = (details) {
|
FlutterError.onError = (details) {
|
||||||
FlutterError.presentError(details);
|
FlutterError.presentError(details);
|
||||||
|
|
|
@ -133,6 +133,7 @@ class _MobileLayout extends StatelessWidget {
|
||||||
).tr(),
|
).tr(),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
setting.subtitle,
|
setting.subtitle,
|
||||||
|
style: context.textTheme.labelLarge,
|
||||||
).tr(),
|
).tr(),
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
context.pushRoute(SettingsSubRoute(section: setting)),
|
context.pushRoute(SettingsSubRoute(section: setting)),
|
||||||
|
|
|
@ -264,7 +264,7 @@ class MapPage extends HookConsumerWidget {
|
||||||
selectedAssets.value = selected ? selection : {};
|
selectedAssets.value = selected ? selection : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapThemeOveride(
|
return MapThemeOverride(
|
||||||
mapBuilder: (style) => context.isMobile
|
mapBuilder: (style) => context.isMobile
|
||||||
// Single-column
|
// Single-column
|
||||||
? Scaffold(
|
? Scaffold(
|
||||||
|
|
|
@ -58,7 +58,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
||||||
controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng));
|
controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapThemeOveride(
|
return MapThemeOverride(
|
||||||
mapBuilder: (style) => Builder(
|
mapBuilder: (style) => Builder(
|
||||||
builder: (ctx) => Scaffold(
|
builder: (ctx) => Scaffold(
|
||||||
backgroundColor: ctx.themeData.cardColor,
|
backgroundColor: ctx.themeData.cardColor,
|
||||||
|
|
74
mobile/lib/providers/theme.provider.dart
Normal file
74
mobile/lib/providers/theme.provider.dart
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
|
import 'package:immich_mobile/theme/color_scheme.dart';
|
||||||
|
import 'package:immich_mobile/theme/theme_data.dart';
|
||||||
|
import 'package:immich_mobile/theme/dynamic_theme.dart';
|
||||||
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
|
|
||||||
|
final immichThemeModeProvider = StateProvider<ThemeMode>((ref) {
|
||||||
|
final themeMode = ref
|
||||||
|
.watch(appSettingsServiceProvider)
|
||||||
|
.getSetting(AppSettingsEnum.themeMode);
|
||||||
|
|
||||||
|
debugPrint("Current themeMode $themeMode");
|
||||||
|
|
||||||
|
if (themeMode == ThemeMode.light.name) {
|
||||||
|
return ThemeMode.light;
|
||||||
|
} else if (themeMode == ThemeMode.dark.name) {
|
||||||
|
return ThemeMode.dark;
|
||||||
|
} else {
|
||||||
|
return ThemeMode.system;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final immichThemePresetProvider = StateProvider<ImmichColorPreset>((ref) {
|
||||||
|
final appSettingsProvider = ref.watch(appSettingsServiceProvider);
|
||||||
|
final primaryColorPreset =
|
||||||
|
appSettingsProvider.getSetting(AppSettingsEnum.primaryColor);
|
||||||
|
|
||||||
|
debugPrint("Current theme preset $primaryColorPreset");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ImmichColorPreset.values
|
||||||
|
.firstWhere((e) => e.name == primaryColorPreset);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint(
|
||||||
|
"Theme preset $primaryColorPreset not found. Applying default preset.",
|
||||||
|
);
|
||||||
|
appSettingsProvider.setSetting(
|
||||||
|
AppSettingsEnum.primaryColor,
|
||||||
|
defaultColorPresetName,
|
||||||
|
);
|
||||||
|
return defaultColorPreset;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final dynamicThemeSettingProvider = StateProvider<bool>((ref) {
|
||||||
|
return ref
|
||||||
|
.watch(appSettingsServiceProvider)
|
||||||
|
.getSetting(AppSettingsEnum.dynamicTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
final colorfulInterfaceSettingProvider = StateProvider<bool>((ref) {
|
||||||
|
return ref
|
||||||
|
.watch(appSettingsServiceProvider)
|
||||||
|
.getSetting(AppSettingsEnum.colorfulInterface);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Provider for current selected theme
|
||||||
|
final immichThemeProvider = StateProvider<ImmichTheme>((ref) {
|
||||||
|
final primaryColorPreset = ref.read(immichThemePresetProvider);
|
||||||
|
final useSystemColor = ref.watch(dynamicThemeSettingProvider);
|
||||||
|
final useColorfulInterface = ref.watch(colorfulInterfaceSettingProvider);
|
||||||
|
final ImmichTheme? dynamicTheme = DynamicTheme.theme;
|
||||||
|
final currentTheme = (useSystemColor && dynamicTheme != null)
|
||||||
|
? dynamicTheme
|
||||||
|
: primaryColorPreset.themeOfPreset;
|
||||||
|
|
||||||
|
return useColorfulInterface
|
||||||
|
? currentTheme
|
||||||
|
: decolorizeSurfaces(theme: currentTheme);
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
|
||||||
enum AppSettingsEnum<T> {
|
enum AppSettingsEnum<T> {
|
||||||
|
|
|
@ -1,29 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
|
import 'package:immich_mobile/theme/theme_data.dart';
|
||||||
|
|
||||||
enum ImmichColorPreset {
|
final Map<ImmichColorPreset, ImmichTheme> _themePresets = {
|
||||||
indigo,
|
|
||||||
deepPurple,
|
|
||||||
pink,
|
|
||||||
red,
|
|
||||||
orange,
|
|
||||||
yellow,
|
|
||||||
lime,
|
|
||||||
green,
|
|
||||||
cyan,
|
|
||||||
slateGray
|
|
||||||
}
|
|
||||||
|
|
||||||
const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo;
|
|
||||||
const String defaultColorPresetName = "indigo";
|
|
||||||
|
|
||||||
const Color immichBrandColorLight = Color(0xFF4150AF);
|
|
||||||
const Color immichBrandColorDark = Color(0xFFACCBFA);
|
|
||||||
const Color whiteOpacity75 = Color.fromARGB((0.75 * 255) ~/ 1, 255, 255, 255);
|
|
||||||
const Color red400 = Color(0xFFEF5350);
|
|
||||||
const Color grey200 = Color(0xFFEEEEEE);
|
|
||||||
|
|
||||||
final Map<ImmichColorPreset, ImmichTheme> _themePresetsMap = {
|
|
||||||
ImmichColorPreset.indigo: ImmichTheme(
|
ImmichColorPreset.indigo: ImmichTheme(
|
||||||
light: ColorScheme.fromSeed(
|
light: ColorScheme.fromSeed(
|
||||||
seedColor: immichBrandColorLight,
|
seedColor: immichBrandColorLight,
|
||||||
|
@ -110,5 +89,5 @@ final Map<ImmichColorPreset, ImmichTheme> _themePresetsMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
extension ImmichColorModeExtension on ImmichColorPreset {
|
extension ImmichColorModeExtension on ImmichColorPreset {
|
||||||
ImmichTheme getTheme() => _themePresetsMap[this]!;
|
ImmichTheme get themeOfPreset => _themePresets[this]!;
|
||||||
}
|
}
|
38
mobile/lib/theme/dynamic_theme.dart
Normal file
38
mobile/lib/theme/dynamic_theme.dart
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/theme/theme_data.dart';
|
||||||
|
|
||||||
|
abstract final class DynamicTheme {
|
||||||
|
DynamicTheme._();
|
||||||
|
|
||||||
|
static ImmichTheme? _theme;
|
||||||
|
// Method to fetch dynamic system colors
|
||||||
|
static Future<void> fetchSystemPalette() async {
|
||||||
|
try {
|
||||||
|
final corePalette = await DynamicColorPlugin.getCorePalette();
|
||||||
|
if (corePalette != null) {
|
||||||
|
final primaryColor = corePalette.toColorScheme().primary;
|
||||||
|
debugPrint('dynamic_color: Core palette detected.');
|
||||||
|
|
||||||
|
// Some palettes do not generate surface container colors accurately,
|
||||||
|
// so we regenerate all colors using the primary color
|
||||||
|
_theme = ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(
|
||||||
|
seedColor: primaryColor,
|
||||||
|
brightness: Brightness.light,
|
||||||
|
),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: primaryColor,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('dynamic_color: Failed to obtain core palette: $error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImmichTheme? get theme => _theme;
|
||||||
|
static bool get isAvailable => _theme != null;
|
||||||
|
}
|
|
@ -1,11 +1,7 @@
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
|
||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
|
|
||||||
class ImmichTheme {
|
class ImmichTheme {
|
||||||
final ColorScheme light;
|
final ColorScheme light;
|
||||||
|
@ -14,104 +10,166 @@ class ImmichTheme {
|
||||||
const ImmichTheme({required this.light, required this.dark});
|
const ImmichTheme({required this.light, required this.dark});
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmichTheme? _immichDynamicTheme;
|
ThemeData getThemeData({
|
||||||
bool get isDynamicThemeAvailable => _immichDynamicTheme != null;
|
required ColorScheme colorScheme,
|
||||||
|
required Locale locale,
|
||||||
|
}) {
|
||||||
|
final isDark = colorScheme.brightness == Brightness.dark;
|
||||||
|
|
||||||
final immichThemeModeProvider = StateProvider<ThemeMode>((ref) {
|
return ThemeData(
|
||||||
var themeMode = ref
|
useMaterial3: true,
|
||||||
.watch(appSettingsServiceProvider)
|
brightness: colorScheme.brightness,
|
||||||
.getSetting(AppSettingsEnum.themeMode);
|
colorScheme: colorScheme,
|
||||||
|
primaryColor: colorScheme.primary,
|
||||||
debugPrint("Current themeMode $themeMode");
|
hintColor: colorScheme.onSurfaceSecondary,
|
||||||
|
focusColor: colorScheme.primary,
|
||||||
if (themeMode == "light") {
|
scaffoldBackgroundColor: colorScheme.surface,
|
||||||
return ThemeMode.light;
|
splashColor: colorScheme.primary.withOpacity(0.1),
|
||||||
} else if (themeMode == "dark") {
|
highlightColor: colorScheme.primary.withOpacity(0.1),
|
||||||
return ThemeMode.dark;
|
dialogBackgroundColor: colorScheme.surfaceContainer,
|
||||||
} else {
|
bottomSheetTheme: BottomSheetThemeData(
|
||||||
return ThemeMode.system;
|
backgroundColor: colorScheme.surfaceContainer,
|
||||||
}
|
),
|
||||||
});
|
fontFamily: _getFontFamilyFromLocale(locale),
|
||||||
|
snackBarTheme: SnackBarThemeData(
|
||||||
final immichThemePresetProvider = StateProvider<ImmichColorPreset>((ref) {
|
contentTextStyle: TextStyle(
|
||||||
var appSettingsProvider = ref.watch(appSettingsServiceProvider);
|
fontFamily: _getFontFamilyFromLocale(locale),
|
||||||
var primaryColorName =
|
color: colorScheme.primary,
|
||||||
appSettingsProvider.getSetting(AppSettingsEnum.primaryColor);
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
debugPrint("Current theme preset $primaryColorName");
|
backgroundColor: colorScheme.surfaceContainerHighest,
|
||||||
|
),
|
||||||
try {
|
appBarTheme: AppBarTheme(
|
||||||
return ImmichColorPreset.values
|
titleTextStyle: TextStyle(
|
||||||
.firstWhere((e) => e.name == primaryColorName);
|
color: colorScheme.primary,
|
||||||
} catch (e) {
|
fontFamily: _getFontFamilyFromLocale(locale),
|
||||||
debugPrint(
|
fontWeight: FontWeight.bold,
|
||||||
"Theme preset $primaryColorName not found. Applying default preset.",
|
fontSize: 18,
|
||||||
);
|
),
|
||||||
appSettingsProvider.setSetting(
|
backgroundColor:
|
||||||
AppSettingsEnum.primaryColor,
|
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
||||||
defaultColorPresetName,
|
foregroundColor: colorScheme.primary,
|
||||||
);
|
elevation: 0,
|
||||||
return defaultColorPreset;
|
scrolledUnderElevation: 0,
|
||||||
}
|
centerTitle: true,
|
||||||
});
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
final dynamicThemeSettingProvider = StateProvider<bool>((ref) {
|
displayLarge: TextStyle(
|
||||||
return ref
|
fontSize: 26,
|
||||||
.watch(appSettingsServiceProvider)
|
fontWeight: FontWeight.bold,
|
||||||
.getSetting(AppSettingsEnum.dynamicTheme);
|
),
|
||||||
});
|
displayMedium: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
final colorfulInterfaceSettingProvider = StateProvider<bool>((ref) {
|
fontWeight: FontWeight.bold,
|
||||||
return ref
|
),
|
||||||
.watch(appSettingsServiceProvider)
|
displaySmall: TextStyle(
|
||||||
.getSetting(AppSettingsEnum.colorfulInterface);
|
fontSize: 12,
|
||||||
});
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
// Provider for current selected theme
|
titleSmall: TextStyle(
|
||||||
final immichThemeProvider = StateProvider<ImmichTheme>((ref) {
|
fontSize: 16.0,
|
||||||
var primaryColor = ref.read(immichThemePresetProvider);
|
fontWeight: FontWeight.bold,
|
||||||
var useSystemColor = ref.watch(dynamicThemeSettingProvider);
|
),
|
||||||
var useColorfulInterface = ref.watch(colorfulInterfaceSettingProvider);
|
titleMedium: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
var currentTheme = (useSystemColor && _immichDynamicTheme != null)
|
fontWeight: FontWeight.bold,
|
||||||
? _immichDynamicTheme!
|
),
|
||||||
: primaryColor.getTheme();
|
titleLarge: TextStyle(
|
||||||
|
fontSize: 26.0,
|
||||||
return useColorfulInterface
|
fontWeight: FontWeight.bold,
|
||||||
? currentTheme
|
),
|
||||||
: _decolorizeSurfaces(theme: currentTheme);
|
),
|
||||||
});
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
// Method to fetch dynamic system colors
|
backgroundColor: colorScheme.primary,
|
||||||
Future<void> fetchSystemPalette() async {
|
foregroundColor: isDark ? Colors.black87 : Colors.white,
|
||||||
try {
|
),
|
||||||
final corePalette = await DynamicColorPlugin.getCorePalette();
|
),
|
||||||
if (corePalette != null) {
|
chipTheme: const ChipThemeData(
|
||||||
final primaryColor = corePalette.toColorScheme().primary;
|
side: BorderSide.none,
|
||||||
debugPrint('dynamic_color: Core palette detected.');
|
),
|
||||||
|
sliderTheme: const SliderThemeData(
|
||||||
// Some palettes do not generate surface container colors accurately,
|
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||||
// so we regenerate all colors using the primary color
|
trackHeight: 2.0,
|
||||||
_immichDynamicTheme = ImmichTheme(
|
),
|
||||||
light: ColorScheme.fromSeed(
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
seedColor: primaryColor,
|
type: BottomNavigationBarType.fixed,
|
||||||
brightness: Brightness.light,
|
),
|
||||||
|
popupMenuTheme: const PopupMenuThemeData(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
|
backgroundColor:
|
||||||
|
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
||||||
|
labelTextStyle: const WidgetStatePropertyAll(
|
||||||
|
TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
dark: ColorScheme.fromSeed(
|
),
|
||||||
seedColor: primaryColor,
|
),
|
||||||
brightness: Brightness.dark,
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
);
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||||
}
|
),
|
||||||
} catch (e) {
|
enabledBorder: OutlineInputBorder(
|
||||||
debugPrint('dynamic_color: Failed to obtain core palette.');
|
borderSide: BorderSide(
|
||||||
}
|
color: colorScheme.outlineVariant,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||||
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
hintStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textSelectionTheme: TextSelectionThemeData(
|
||||||
|
cursorColor: colorScheme.primary,
|
||||||
|
),
|
||||||
|
dropdownMenuTheme: DropdownMenuThemeData(
|
||||||
|
menuStyle: const MenuStyle(
|
||||||
|
shape: WidgetStatePropertyAll<OutlinedBorder>(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(15)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: colorScheme.outlineVariant,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||||
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
hintStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method replaces all surface shades in ImmichTheme to a static ones
|
// This method replaces all surface shades in ImmichTheme to a static ones
|
||||||
// as we are creating the colorscheme through seedColor the default surfaces are
|
// as we are creating the colorscheme through seedColor the default surfaces are
|
||||||
// tinted with primary color
|
// tinted with primary color
|
||||||
ImmichTheme _decolorizeSurfaces({
|
ImmichTheme decolorizeSurfaces({
|
||||||
required ImmichTheme theme,
|
required ImmichTheme theme,
|
||||||
}) {
|
}) {
|
||||||
return ImmichTheme(
|
return ImmichTheme(
|
||||||
|
@ -146,167 +204,10 @@ ImmichTheme _decolorizeSurfaces({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? getFontFamilyFromLocale(Locale locale) {
|
String? _getFontFamilyFromLocale(Locale locale) {
|
||||||
if (localesNotSupportedByOverpass.contains(locale)) {
|
if (localesNotSupportedByOverpass.contains(locale)) {
|
||||||
// Let Flutter use the default font
|
// Let Flutter use the default font
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return 'Overpass';
|
return 'Overpass';
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeData getThemeData({
|
|
||||||
required ColorScheme colorScheme,
|
|
||||||
required Locale locale,
|
|
||||||
}) {
|
|
||||||
var isDark = colorScheme.brightness == Brightness.dark;
|
|
||||||
var primaryColor = colorScheme.primary;
|
|
||||||
|
|
||||||
return ThemeData(
|
|
||||||
useMaterial3: true,
|
|
||||||
brightness: colorScheme.brightness,
|
|
||||||
colorScheme: colorScheme,
|
|
||||||
primaryColor: primaryColor,
|
|
||||||
hintColor: colorScheme.onSurfaceSecondary,
|
|
||||||
focusColor: primaryColor,
|
|
||||||
scaffoldBackgroundColor: colorScheme.surface,
|
|
||||||
splashColor: primaryColor.withOpacity(0.1),
|
|
||||||
highlightColor: primaryColor.withOpacity(0.1),
|
|
||||||
dialogBackgroundColor: colorScheme.surfaceContainer,
|
|
||||||
bottomSheetTheme: BottomSheetThemeData(
|
|
||||||
backgroundColor: colorScheme.surfaceContainer,
|
|
||||||
),
|
|
||||||
fontFamily: getFontFamilyFromLocale(locale),
|
|
||||||
snackBarTheme: SnackBarThemeData(
|
|
||||||
contentTextStyle: TextStyle(
|
|
||||||
fontFamily: getFontFamilyFromLocale(locale),
|
|
||||||
color: primaryColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
backgroundColor: colorScheme.surfaceContainerHighest,
|
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
fontFamily: getFontFamilyFromLocale(locale),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
backgroundColor:
|
|
||||||
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
|
||||||
foregroundColor: primaryColor,
|
|
||||||
elevation: 0,
|
|
||||||
scrolledUnderElevation: 0,
|
|
||||||
centerTitle: true,
|
|
||||||
),
|
|
||||||
textTheme: const TextTheme(
|
|
||||||
displayLarge: TextStyle(
|
|
||||||
fontSize: 26,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
displayMedium: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
displaySmall: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
titleSmall: TextStyle(
|
|
||||||
fontSize: 16.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
titleMedium: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
titleLarge: TextStyle(
|
|
||||||
fontSize: 26.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
foregroundColor: isDark ? Colors.black87 : Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
chipTheme: const ChipThemeData(
|
|
||||||
side: BorderSide.none,
|
|
||||||
),
|
|
||||||
sliderTheme: const SliderThemeData(
|
|
||||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
|
||||||
trackHeight: 2.0,
|
|
||||||
),
|
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
|
||||||
type: BottomNavigationBarType.fixed,
|
|
||||||
),
|
|
||||||
popupMenuTheme: const PopupMenuThemeData(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
|
||||||
backgroundColor:
|
|
||||||
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
|
||||||
labelTextStyle: const WidgetStatePropertyAll(
|
|
||||||
TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.outlineVariant,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
||||||
),
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
hintStyle: const TextStyle(
|
|
||||||
fontSize: 14.0,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textSelectionTheme: TextSelectionThemeData(
|
|
||||||
cursorColor: primaryColor,
|
|
||||||
),
|
|
||||||
dropdownMenuTheme: DropdownMenuThemeData(
|
|
||||||
menuStyle: MenuStyle(
|
|
||||||
shape: WidgetStatePropertyAll<OutlinedBorder>(
|
|
||||||
RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: colorScheme.outlineVariant,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
|
||||||
),
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
hintStyle: const TextStyle(
|
|
||||||
fontSize: 14.0,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
|
|
||||||
class MotionPhotoButton extends ConsumerWidget {
|
class MotionPhotoButton extends ConsumerWidget {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_viewer/formatted_duration.dart';
|
import 'package:immich_mobile/widgets/asset_viewer/formatted_duration.dart';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/backup/error_chip_text.dart';
|
import 'package:immich_mobile/widgets/backup/error_chip_text.dart';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
||||||
|
|
||||||
class BackupErrorChipText extends ConsumerWidget {
|
class BackupErrorChipText extends ConsumerWidget {
|
||||||
|
|
|
@ -3,21 +3,22 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/providers/map/map_state.provider.dart';
|
import 'package:immich_mobile/providers/map/map_state.provider.dart';
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
import 'package:immich_mobile/providers/theme.provider.dart';
|
||||||
|
import 'package:immich_mobile/theme/theme_data.dart';
|
||||||
|
|
||||||
/// Overrides the theme below the widget tree to use the theme data based on the
|
/// Overrides the theme below the widget tree to use the theme data based on the
|
||||||
/// map settings instead of the one from the app settings
|
/// map settings instead of the one from the app settings
|
||||||
class MapThemeOveride extends StatefulHookConsumerWidget {
|
class MapThemeOverride extends StatefulHookConsumerWidget {
|
||||||
final ThemeMode? themeMode;
|
final ThemeMode? themeMode;
|
||||||
final Widget Function(AsyncValue<String> style) mapBuilder;
|
final Widget Function(AsyncValue<String> style) mapBuilder;
|
||||||
|
|
||||||
const MapThemeOveride({required this.mapBuilder, this.themeMode, super.key});
|
const MapThemeOverride({required this.mapBuilder, this.themeMode, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState createState() => _MapThemeOverideState();
|
ConsumerState createState() => _MapThemeOverrideState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MapThemeOverideState extends ConsumerState<MapThemeOveride>
|
class _MapThemeOverrideState extends ConsumerState<MapThemeOverride>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
late ThemeMode _theme;
|
late ThemeMode _theme;
|
||||||
bool _isDarkTheme = false;
|
bool _isDarkTheme = false;
|
||||||
|
|
|
@ -62,7 +62,7 @@ class MapThumbnail extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapThemeOveride(
|
return MapThemeOverride(
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
mapBuilder: (style) => SizedBox(
|
mapBuilder: (style) => SizedBox(
|
||||||
height: height,
|
height: height,
|
||||||
|
|
|
@ -2,12 +2,14 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
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/theme.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
|
||||||
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
||||||
|
import 'package:immich_mobile/theme/color_scheme.dart';
|
||||||
|
import 'package:immich_mobile/theme/dynamic_theme.dart';
|
||||||
|
|
||||||
class PrimaryColorSetting extends HookConsumerWidget {
|
class PrimaryColorSetting extends HookConsumerWidget {
|
||||||
const PrimaryColorSetting({
|
const PrimaryColorSetting({
|
||||||
|
@ -124,7 +126,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
|
||||||
style: context.textTheme.titleLarge,
|
style: context.textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isDynamicThemeAvailable)
|
if (DynamicTheme.isAvailable)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
margin: const EdgeInsets.only(top: 10),
|
margin: const EdgeInsets.only(top: 10),
|
||||||
|
@ -153,16 +155,16 @@ class PrimaryColorSetting extends HookConsumerWidget {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: ImmichColorPreset.values.map((themePreset) {
|
children: ImmichColorPreset.values.map((preset) {
|
||||||
var theme = themePreset.getTheme();
|
final theme = preset.themeOfPreset;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => onPrimaryColorChange(themePreset),
|
onTap: () => onPrimaryColorChange(preset),
|
||||||
child: buildPrimaryColorTile(
|
child: buildPrimaryColorTile(
|
||||||
topColor: theme.light.primary,
|
topColor: theme.light.primary,
|
||||||
bottomColor: theme.dark.primary,
|
bottomColor: theme.dark.primary,
|
||||||
tileSize: tileSize,
|
tileSize: tileSize,
|
||||||
showSelector: currentPreset.value == themePreset &&
|
showSelector: currentPreset.value == preset &&
|
||||||
!systemPrimaryColorSetting.value,
|
!systemPrimaryColorSetting.value,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,12 +3,12 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/theme.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart';
|
import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/settings_sub_title.dart';
|
import 'package:immich_mobile/widgets/settings/settings_sub_title.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
|
||||||
|
|
||||||
class ThemeSetting extends HookConsumerWidget {
|
class ThemeSetting extends HookConsumerWidget {
|
||||||
const ThemeSetting({
|
const ThemeSetting({
|
||||||
|
|
|
@ -35,7 +35,7 @@ void main() {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
@ -53,7 +53,7 @@ void main() {
|
||||||
testWidgets("Return error when style is not fetched", (tester) async {
|
testWidgets("Return error when style is not fetched", (tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
@ -73,7 +73,7 @@ void main() {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
@ -94,7 +94,7 @@ void main() {
|
||||||
testWidgets("Return dark theme style when system is dark", (tester) async {
|
testWidgets("Return dark theme style when system is dark", (tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
@ -118,7 +118,7 @@ void main() {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
@ -142,7 +142,7 @@ void main() {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
AsyncValue<String>? mapStyle;
|
AsyncValue<String>? mapStyle;
|
||||||
await tester.pumpConsumerWidget(
|
await tester.pumpConsumerWidget(
|
||||||
MapThemeOveride(
|
MapThemeOverride(
|
||||||
mapBuilder: (AsyncValue<String> style) {
|
mapBuilder: (AsyncValue<String> style) {
|
||||||
mapStyle = style;
|
mapStyle = style;
|
||||||
return const Text("Mock");
|
return const Text("Mock");
|
||||||
|
|
Loading…
Reference in a new issue