1
0
Fork 0
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:
dvbthien 2024-12-11 23:30:56 +07:00 committed by GitHub
parent 5814a1b223
commit 11f585d0ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 343 additions and 322 deletions

View 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);

View file

@ -4,23 +4,26 @@ import 'dart:io';
import 'package:background_downloader/background_downloader.dart';
import 'package:device_info_plus/device_info_plus.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/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/services/background.service.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
import 'package:immich_mobile/providers/locale_provider.dart';
import 'package:immich_mobile/providers/theme.provider.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/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/android_device_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/store.entity.dart';
import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/services/background.service.dart';
import 'package:immich_mobile/services/immich_logger.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:isar/isar.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:immich_mobile/utils/download.dart';
import 'package:immich_mobile/utils/cache/widgets_binding.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 {
ImmichWidgetsBinding();
@ -69,12 +71,12 @@ Future<void> initApp() async {
}
}
await fetchSystemPalette();
await DynamicTheme.fetchSystemPalette();
// Initialize Immich Logger Service
ImmichLogger();
var log = Logger("ImmichErrorLogger");
final log = Logger("ImmichErrorLogger");
FlutterError.onError = (details) {
FlutterError.presentError(details);

View file

@ -133,6 +133,7 @@ class _MobileLayout extends StatelessWidget {
).tr(),
subtitle: Text(
setting.subtitle,
style: context.textTheme.labelLarge,
).tr(),
onTap: () =>
context.pushRoute(SettingsSubRoute(section: setting)),

View file

@ -264,7 +264,7 @@ class MapPage extends HookConsumerWidget {
selectedAssets.value = selected ? selection : {};
}
return MapThemeOveride(
return MapThemeOverride(
mapBuilder: (style) => context.isMobile
// Single-column
? Scaffold(

View file

@ -58,7 +58,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng));
}
return MapThemeOveride(
return MapThemeOverride(
mapBuilder: (style) => Builder(
builder: (ctx) => Scaffold(
backgroundColor: ctx.themeData.cardColor,

View 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);
});

View file

@ -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';
enum AppSettingsEnum<T> {

View file

@ -1,29 +1,8 @@
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 {
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 = {
final Map<ImmichColorPreset, ImmichTheme> _themePresets = {
ImmichColorPreset.indigo: ImmichTheme(
light: ColorScheme.fromSeed(
seedColor: immichBrandColorLight,
@ -110,5 +89,5 @@ final Map<ImmichColorPreset, ImmichTheme> _themePresetsMap = {
};
extension ImmichColorModeExtension on ImmichColorPreset {
ImmichTheme getTheme() => _themePresetsMap[this]!;
ImmichTheme get themeOfPreset => _themePresets[this]!;
}

View 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;
}

View file

@ -1,11 +1,7 @@
import 'package:dynamic_color/dynamic_color.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/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
class ImmichTheme {
final ColorScheme light;
@ -14,104 +10,166 @@ class ImmichTheme {
const ImmichTheme({required this.light, required this.dark});
}
ImmichTheme? _immichDynamicTheme;
bool get isDynamicThemeAvailable => _immichDynamicTheme != null;
ThemeData getThemeData({
required ColorScheme colorScheme,
required Locale locale,
}) {
final isDark = colorScheme.brightness == Brightness.dark;
final immichThemeModeProvider = StateProvider<ThemeMode>((ref) {
var themeMode = ref
.watch(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.themeMode);
debugPrint("Current themeMode $themeMode");
if (themeMode == "light") {
return ThemeMode.light;
} else if (themeMode == "dark") {
return ThemeMode.dark;
} else {
return ThemeMode.system;
}
});
final immichThemePresetProvider = StateProvider<ImmichColorPreset>((ref) {
var appSettingsProvider = ref.watch(appSettingsServiceProvider);
var primaryColorName =
appSettingsProvider.getSetting(AppSettingsEnum.primaryColor);
debugPrint("Current theme preset $primaryColorName");
try {
return ImmichColorPreset.values
.firstWhere((e) => e.name == primaryColorName);
} catch (e) {
debugPrint(
"Theme preset $primaryColorName 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) {
var primaryColor = ref.read(immichThemePresetProvider);
var useSystemColor = ref.watch(dynamicThemeSettingProvider);
var useColorfulInterface = ref.watch(colorfulInterfaceSettingProvider);
var currentTheme = (useSystemColor && _immichDynamicTheme != null)
? _immichDynamicTheme!
: primaryColor.getTheme();
return useColorfulInterface
? currentTheme
: _decolorizeSurfaces(theme: currentTheme);
});
// Method to fetch dynamic system colors
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
_immichDynamicTheme = ImmichTheme(
light: ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: Brightness.light,
return ThemeData(
useMaterial3: true,
brightness: colorScheme.brightness,
colorScheme: colorScheme,
primaryColor: colorScheme.primary,
hintColor: colorScheme.onSurfaceSecondary,
focusColor: colorScheme.primary,
scaffoldBackgroundColor: colorScheme.surface,
splashColor: colorScheme.primary.withOpacity(0.1),
highlightColor: colorScheme.primary.withOpacity(0.1),
dialogBackgroundColor: colorScheme.surfaceContainer,
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: colorScheme.surfaceContainer,
),
fontFamily: _getFontFamilyFromLocale(locale),
snackBarTheme: SnackBarThemeData(
contentTextStyle: TextStyle(
fontFamily: _getFontFamilyFromLocale(locale),
color: colorScheme.primary,
fontWeight: FontWeight.bold,
),
backgroundColor: colorScheme.surfaceContainerHighest,
),
appBarTheme: AppBarTheme(
titleTextStyle: TextStyle(
color: colorScheme.primary,
fontFamily: _getFontFamilyFromLocale(locale),
fontWeight: FontWeight.bold,
fontSize: 18,
),
backgroundColor:
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
foregroundColor: colorScheme.primary,
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: colorScheme.primary,
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: colorScheme.primary,
),
borderRadius: const BorderRadius.all(Radius.circular(15)),
),
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,
),
),
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,
),
),
dark: ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: Brightness.dark,
),
);
}
} catch (e) {
debugPrint('dynamic_color: Failed to obtain core palette.');
}
}
// This method replaces all surface shades in ImmichTheme to a static ones
// as we are creating the colorscheme through seedColor the default surfaces are
// tinted with primary color
ImmichTheme _decolorizeSurfaces({
ImmichTheme decolorizeSurfaces({
required ImmichTheme theme,
}) {
return ImmichTheme(
@ -146,167 +204,10 @@ ImmichTheme _decolorizeSurfaces({
);
}
String? getFontFamilyFromLocale(Locale locale) {
String? _getFontFamilyFromLocale(Locale locale) {
if (localesNotSupportedByOverpass.contains(locale)) {
// Let Flutter use the default font
return null;
}
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,
),
),
),
);
}

View file

@ -1,6 +1,6 @@
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/colors.dart';
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
class MotionPhotoButton extends ConsumerWidget {

View file

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.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_value_provider.dart';
import 'package:immich_mobile/widgets/asset_viewer/formatted_duration.dart';

View file

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.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/colors.dart';
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/backup/error_chip_text.dart';

View file

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.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/colors.dart';
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
class BackupErrorChipText extends ConsumerWidget {

View file

@ -3,21 +3,22 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/providers/locale_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
/// map settings instead of the one from the app settings
class MapThemeOveride extends StatefulHookConsumerWidget {
class MapThemeOverride extends StatefulHookConsumerWidget {
final ThemeMode? themeMode;
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
ConsumerState createState() => _MapThemeOverideState();
ConsumerState createState() => _MapThemeOverrideState();
}
class _MapThemeOverideState extends ConsumerState<MapThemeOveride>
class _MapThemeOverrideState extends ConsumerState<MapThemeOverride>
with WidgetsBindingObserver {
late ThemeMode _theme;
bool _isDarkTheme = false;

View file

@ -62,7 +62,7 @@ class MapThumbnail extends HookConsumerWidget {
}
}
return MapThemeOveride(
return MapThemeOverride(
themeMode: themeMode,
mapBuilder: (style) => SizedBox(
height: height,

View file

@ -2,12 +2,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/immich_colors.dart';
import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/extensions/build_context_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/utils/immich_app_theme.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 {
const PrimaryColorSetting({
@ -124,7 +126,7 @@ class PrimaryColorSetting extends HookConsumerWidget {
style: context.textTheme.titleLarge,
),
),
if (isDynamicThemeAvailable)
if (DynamicTheme.isAvailable)
Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
margin: const EdgeInsets.only(top: 10),
@ -153,16 +155,16 @@ class PrimaryColorSetting extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: ImmichColorPreset.values.map((themePreset) {
var theme = themePreset.getTheme();
children: ImmichColorPreset.values.map((preset) {
final theme = preset.themeOfPreset;
return GestureDetector(
onTap: () => onPrimaryColorChange(themePreset),
onTap: () => onPrimaryColorChange(preset),
child: buildPrimaryColorTile(
topColor: theme.light.primary,
bottomColor: theme.dark.primary,
tileSize: tileSize,
showSelector: currentPreset.value == themePreset &&
showSelector: currentPreset.value == preset &&
!systemPrimaryColorSetting.value,
),
);

View file

@ -3,12 +3,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/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_switch_list_tile.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 {
const ThemeSetting({

View file

@ -35,7 +35,7 @@ void main() {
(tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");
@ -53,7 +53,7 @@ void main() {
testWidgets("Return error when style is not fetched", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");
@ -73,7 +73,7 @@ void main() {
(tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");
@ -94,7 +94,7 @@ void main() {
testWidgets("Return dark theme style when system is dark", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");
@ -118,7 +118,7 @@ void main() {
(tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");
@ -142,7 +142,7 @@ void main() {
(tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOveride(
MapThemeOverride(
mapBuilder: (AsyncValue<String> style) {
mapStyle = style;
return const Text("Mock");