mirror of
https://github.com/immich-app/immich.git
synced 2025-01-17 01:06:46 +01:00
feat(mobile): add support for material themes (#11560)
* feat(mobile): add support for material themes Added support for custom theming and updated all elements accordingly. * fix(mobile): Restored immich brand colors to default theme * fix(mobile): make ListTile titles bold in settings main page * feat(mobile): update bottom nav and appbar colors * small tweaks --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
20262209ce
commit
0eacdf93eb
65 changed files with 944 additions and 563 deletions
|
@ -531,6 +531,11 @@
|
||||||
"theme_setting_dark_mode_switch": "Dark mode",
|
"theme_setting_dark_mode_switch": "Dark mode",
|
||||||
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
|
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
|
||||||
"theme_setting_image_viewer_quality_title": "Image viewer quality",
|
"theme_setting_image_viewer_quality_title": "Image viewer quality",
|
||||||
|
"theme_setting_primary_color_title": "Primary color",
|
||||||
|
"theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.",
|
||||||
|
"theme_setting_colorful_interface_title": "Colorful interface",
|
||||||
|
"theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
|
||||||
|
"theme_setting_system_primary_color_title": "Use system color",
|
||||||
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
"theme_setting_system_theme_switch": "Automatic (Follow system setting)",
|
||||||
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
"theme_setting_theme_subtitle": "Choose the app's theme setting",
|
||||||
"theme_setting_theme_title": "Theme",
|
"theme_setting_theme_title": "Theme",
|
||||||
|
|
|
@ -1,5 +1,108 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||||
|
|
||||||
const Color immichBackgroundColor = Color(0xFFf6f8fe);
|
enum ImmichColorPreset {
|
||||||
const Color immichDarkBackgroundColor = Color.fromARGB(255, 0, 0, 0);
|
indigo,
|
||||||
const Color immichDarkThemePrimaryColor = Color.fromARGB(255, 173, 203, 250);
|
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);
|
||||||
|
|
||||||
|
final Map<ImmichColorPreset, ImmichTheme> _themePresetsMap = {
|
||||||
|
ImmichColorPreset.indigo: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(
|
||||||
|
seedColor: immichBrandColorLight,
|
||||||
|
).copyWith(primary: immichBrandColorLight),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: immichBrandColorDark,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
).copyWith(primary: immichBrandColorDark),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.deepPurple: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFF6F43C0)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFD3BBFF),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.pink: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFED79B5),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.red: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFFC51C16)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFD3302F),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.orange: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xffff5b01),
|
||||||
|
dynamicSchemeVariant: DynamicSchemeVariant.fidelity,
|
||||||
|
),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFCC6D08),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
dynamicSchemeVariant: DynamicSchemeVariant.fidelity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.yellow: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFFFB400),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.lime: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFCDDC39),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.green: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFF18C249),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.cyan: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4)),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFF00BCD4),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImmichColorPreset.slateGray: ImmichTheme(
|
||||||
|
light: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFF696969),
|
||||||
|
dynamicSchemeVariant: DynamicSchemeVariant.neutral,
|
||||||
|
),
|
||||||
|
dark: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xff696969),
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
dynamicSchemeVariant: DynamicSchemeVariant.neutral,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
extension ImmichColorModeExtension on ImmichColorPreset {
|
||||||
|
ImmichTheme getTheme() => _themePresetsMap[this]!;
|
||||||
|
}
|
||||||
|
|
|
@ -229,6 +229,11 @@ enum StoreKey<T> {
|
||||||
mapwithPartners<bool>(125, type: bool),
|
mapwithPartners<bool>(125, type: bool),
|
||||||
enableHapticFeedback<bool>(126, type: bool),
|
enableHapticFeedback<bool>(126, type: bool),
|
||||||
customHeaders<String>(127, type: String),
|
customHeaders<String>(127, type: String),
|
||||||
|
|
||||||
|
// theme settings
|
||||||
|
primaryColor<String>(128, type: String),
|
||||||
|
dynamicTheme<bool>(129, type: bool),
|
||||||
|
colorfulInterface<bool>(130, type: bool),
|
||||||
;
|
;
|
||||||
|
|
||||||
const StoreKey(
|
const StoreKey(
|
||||||
|
|
|
@ -20,10 +20,10 @@ extension ContextHelper on BuildContext {
|
||||||
bool get isDarkTheme => themeData.brightness == Brightness.dark;
|
bool get isDarkTheme => themeData.brightness == Brightness.dark;
|
||||||
|
|
||||||
// Returns the current Primary color of the Theme
|
// Returns the current Primary color of the Theme
|
||||||
Color get primaryColor => themeData.primaryColor;
|
Color get primaryColor => themeData.colorScheme.primary;
|
||||||
|
|
||||||
// Returns the Scaffold background color of the Theme
|
// Returns the Scaffold background color of the Theme
|
||||||
Color get scaffoldBackgroundColor => themeData.scaffoldBackgroundColor;
|
Color get scaffoldBackgroundColor => colorScheme.surface;
|
||||||
|
|
||||||
// Returns the current TextTheme
|
// Returns the current TextTheme
|
||||||
TextTheme get textTheme => themeData.textTheme;
|
TextTheme get textTheme => themeData.textTheme;
|
||||||
|
|
24
mobile/lib/extensions/theme_extensions.dart
Normal file
24
mobile/lib/extensions/theme_extensions.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension ImmichColorSchemeExtensions on ColorScheme {
|
||||||
|
bool get _isDarkMode => brightness == Brightness.dark;
|
||||||
|
Color get onSurfaceSecondary => _isDarkMode
|
||||||
|
? onSurface.darken(amount: .3)
|
||||||
|
: onSurface.lighten(amount: .3);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ColorExtensions on Color {
|
||||||
|
Color lighten({double amount = 0.1}) {
|
||||||
|
return Color.alphaBlend(
|
||||||
|
Colors.white.withOpacity(amount),
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color darken({double amount = 0.1}) {
|
||||||
|
return Color.alphaBlend(
|
||||||
|
Colors.black.withOpacity(amount),
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,6 +65,8 @@ Future<void> initApp() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await fetchSystemPalette();
|
||||||
|
|
||||||
// Initialize Immich Logger Service
|
// Initialize Immich Logger Service
|
||||||
ImmichLogger();
|
ImmichLogger();
|
||||||
|
|
||||||
|
@ -187,6 +189,7 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var router = ref.watch(appRouterProvider);
|
var router = ref.watch(appRouterProvider);
|
||||||
|
var immichTheme = ref.watch(immichThemeProvider);
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
localizationsDelegates: context.localizationDelegates,
|
localizationsDelegates: context.localizationDelegates,
|
||||||
|
@ -196,9 +199,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||||
home: MaterialApp.router(
|
home: MaterialApp.router(
|
||||||
title: 'Immich',
|
title: 'Immich',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
themeMode: ref.watch(immichThemeProvider),
|
themeMode: ref.watch(immichThemeModeProvider),
|
||||||
darkTheme: immichDarkTheme,
|
darkTheme: getThemeData(colorScheme: immichTheme.dark),
|
||||||
theme: immichLightTheme,
|
theme: getThemeData(colorScheme: immichTheme.light),
|
||||||
routeInformationParser: router.defaultRouteParser(),
|
routeInformationParser: router.defaultRouteParser(),
|
||||||
routerDelegate: router.delegate(
|
routerDelegate: router.delegate(
|
||||||
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
||||||
|
|
|
@ -4,6 +4,8 @@ import 'package:auto_route/auto_route.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/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
|
@ -46,7 +48,7 @@ class AlbumPreviewPage extends HookConsumerWidget {
|
||||||
"ID ${album.id}",
|
"ID ${album.id}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: Colors.grey[600],
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,6 @@ 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/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/backup/album_info_card.dart';
|
import 'package:immich_mobile/widgets/backup/album_info_card.dart';
|
||||||
|
@ -128,13 +127,12 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||||
album.name,
|
album.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: isDarkTheme ? Colors.black : immichBackgroundColor,
|
color: context.scaffoldBackgroundColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.red[300],
|
backgroundColor: Colors.red[300],
|
||||||
deleteIconColor:
|
deleteIconColor: context.scaffoldBackgroundColor,
|
||||||
isDarkTheme ? Colors.black : immichBackgroundColor,
|
|
||||||
deleteIcon: const Icon(
|
deleteIcon: const Icon(
|
||||||
Icons.cancel_rounded,
|
Icons.cancel_rounded,
|
||||||
size: 15,
|
size: 15,
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
|
@ -130,9 +131,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.outlineVariant,
|
||||||
? const Color.fromARGB(255, 56, 56, 56)
|
|
||||||
: Colors.black12,
|
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -151,7 +150,9 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"backup_controller_page_to_backup",
|
"backup_controller_page_to_backup",
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
buildSelectedAlbumName(),
|
buildSelectedAlbumName(),
|
||||||
buildExcludedAlbumName(),
|
buildExcludedAlbumName(),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/authentication.provider.dart';
|
import 'package:immich_mobile/providers/authentication.provider.dart';
|
||||||
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
||||||
|
@ -102,7 +103,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
backgroundColor: context.scaffoldBackgroundColor,
|
backgroundColor: context.colorScheme.surfaceContainer,
|
||||||
isScrollControlled: false,
|
isScrollControlled: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
@ -131,7 +132,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
album.owner.value?.email ?? "",
|
album.owner.value?.email ?? "",
|
||||||
style: TextStyle(color: Colors.grey[600]),
|
style: TextStyle(color: context.colorScheme.onSurfaceSecondary),
|
||||||
),
|
),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
"shared_album_section_people_owner_label",
|
"shared_album_section_people_owner_label",
|
||||||
|
@ -160,7 +161,9 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
user.email,
|
user.email,
|
||||||
style: TextStyle(color: Colors.grey[600]),
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
trailing: userId == user.id || isOwner
|
trailing: userId == user.id || isOwner
|
||||||
? const Icon(Icons.more_horiz_rounded)
|
? const Icon(Icons.more_horiz_rounded)
|
||||||
|
@ -214,7 +217,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"shared_album_activity_setting_subtitle",
|
"shared_album_activity_setting_subtitle",
|
||||||
style: context.textTheme.labelLarge?.copyWith(
|
style: context.textTheme.labelLarge?.copyWith(
|
||||||
color: context.textTheme.labelLarge?.color?.withAlpha(175),
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
||||||
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
||||||
import 'package:immich_mobile/services/album.service.dart';
|
import 'package:immich_mobile/services/album.service.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_action_outlined_button.dart';
|
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_viewer_editable_title.dart';
|
import 'package:immich_mobile/widgets/album/album_viewer_editable_title.dart';
|
||||||
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/providers/authentication.provider.dart';
|
import 'package:immich_mobile/providers/authentication.provider.dart';
|
||||||
|
@ -114,13 +114,13 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
AlbumActionOutlinedButton(
|
AlbumActionFilledButton(
|
||||||
iconData: Icons.add_photo_alternate_outlined,
|
iconData: Icons.add_photo_alternate_outlined,
|
||||||
onPressed: () => onAddPhotosPressed(album),
|
onPressed: () => onAddPhotosPressed(album),
|
||||||
labelText: "share_add_photos".tr(),
|
labelText: "share_add_photos".tr(),
|
||||||
),
|
),
|
||||||
if (userId == album.ownerId)
|
if (userId == album.ownerId)
|
||||||
AlbumActionOutlinedButton(
|
AlbumActionFilledButton(
|
||||||
iconData: Icons.person_add_alt_rounded,
|
iconData: Icons.person_add_alt_rounded,
|
||||||
onPressed: () => onAddUsersPressed(album),
|
onPressed: () => onAddUsersPressed(album),
|
||||||
labelText: "album_viewer_page_share_add_users".tr(),
|
labelText: "album_viewer_page_share_add_users".tr(),
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/entities/logger_message.entity.dart';
|
import 'package:immich_mobile/entities/logger_message.entity.dart';
|
||||||
import 'package:immich_mobile/services/immich_logger.service.dart';
|
import 'package:immich_mobile/services/immich_logger.service.dart';
|
||||||
|
@ -18,7 +19,6 @@ class AppLogPage extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final immichLogger = ImmichLogger();
|
final immichLogger = ImmichLogger();
|
||||||
final logMessages = useState(immichLogger.messages);
|
final logMessages = useState(immichLogger.messages);
|
||||||
final isDarkTheme = context.isDarkTheme;
|
|
||||||
|
|
||||||
Widget colorStatusIndicator(Color color) {
|
Widget colorStatusIndicator(Color color) {
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -55,13 +55,9 @@ class AppLogPage extends HookConsumerWidget {
|
||||||
case LogLevel.INFO:
|
case LogLevel.INFO:
|
||||||
return Colors.transparent;
|
return Colors.transparent;
|
||||||
case LogLevel.SEVERE:
|
case LogLevel.SEVERE:
|
||||||
return isDarkTheme
|
return Colors.redAccent.withOpacity(0.25);
|
||||||
? Colors.redAccent.withOpacity(0.25)
|
|
||||||
: Colors.redAccent.withOpacity(0.075);
|
|
||||||
case LogLevel.WARNING:
|
case LogLevel.WARNING:
|
||||||
return isDarkTheme
|
return Colors.orangeAccent.withOpacity(0.25);
|
||||||
? Colors.orangeAccent.withOpacity(0.25)
|
|
||||||
: Colors.orangeAccent.withOpacity(0.075);
|
|
||||||
default:
|
default:
|
||||||
return context.primaryColor.withOpacity(0.1);
|
return context.primaryColor.withOpacity(0.1);
|
||||||
}
|
}
|
||||||
|
@ -120,10 +116,7 @@ class AppLogPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
body: ListView.separated(
|
body: ListView.separated(
|
||||||
separatorBuilder: (context, index) {
|
separatorBuilder: (context, index) {
|
||||||
return Divider(
|
return const Divider(height: 0);
|
||||||
height: 0,
|
|
||||||
color: isDarkTheme ? Colors.white70 : Colors.grey[600],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
itemCount: logMessages.value.length,
|
itemCount: logMessages.value.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
@ -141,8 +134,9 @@ class AppLogPage extends HookConsumerWidget {
|
||||||
minLeadingWidth: 10,
|
minLeadingWidth: 10,
|
||||||
title: Text(
|
title: Text(
|
||||||
truncateLogMessage(logMessage.message, 4),
|
truncateLogMessage(logMessage.message, 4),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
fontFamily: "Inconsolata",
|
fontFamily: "Inconsolata",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -150,7 +144,7 @@ class AppLogPage extends HookConsumerWidget {
|
||||||
"at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.context1}",
|
"at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.context1}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
color: Colors.grey[600],
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
leading: buildLeadingIcon(logMessage.level),
|
leading: buildLeadingIcon(logMessage.level),
|
||||||
|
|
|
@ -13,8 +13,6 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
var isDarkTheme = context.isDarkTheme;
|
|
||||||
|
|
||||||
buildTextWithCopyButton(String header, String text) {
|
buildTextWithCopyButton(String header, String text) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
@ -61,7 +59,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
color: context.colorScheme.surfaceContainerHigh,
|
||||||
borderRadius: BorderRadius.circular(15.0),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -100,7 +98,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
color: context.colorScheme.surfaceContainerHigh,
|
||||||
borderRadius: BorderRadius.circular(15.0),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
@ -10,7 +10,7 @@ import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/album_title.provider.dart';
|
import 'package:immich_mobile/providers/album/album_title.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_action_outlined_button.dart';
|
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
||||||
import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart';
|
import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart';
|
||||||
|
|
||||||
|
@ -109,20 +109,16 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||||
if (selectedAssets.value.isEmpty) {
|
if (selectedAssets.value.isEmpty) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 16, left: 18, right: 18),
|
padding: const EdgeInsets.only(top: 16, left: 16, right: 16),
|
||||||
child: OutlinedButton.icon(
|
child: FilledButton.icon(
|
||||||
style: OutlinedButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
|
const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
||||||
side: BorderSide(
|
|
||||||
color: context.isDarkTheme
|
|
||||||
? const Color.fromARGB(255, 63, 63, 63)
|
|
||||||
: const Color.fromARGB(255, 129, 129, 129),
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainerHighest,
|
||||||
),
|
),
|
||||||
onPressed: onSelectPhotosButtonPressed,
|
onPressed: onSelectPhotosButtonPressed,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
@ -134,7 +130,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
'create_shared_album_page_share_select_photos',
|
'create_shared_album_page_share_select_photos',
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
color: context.primaryColor,
|
fontWeight: FontWeight.normal,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
|
@ -154,7 +150,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
AlbumActionOutlinedButton(
|
AlbumActionFilledButton(
|
||||||
iconData: Icons.add_photo_alternate_outlined,
|
iconData: Icons.add_photo_alternate_outlined,
|
||||||
onPressed: onSelectPhotosButtonPressed,
|
onPressed: onSelectPhotosButtonPressed,
|
||||||
labelText: "share_add_photos".tr(),
|
labelText: "share_add_photos".tr(),
|
||||||
|
|
|
@ -49,10 +49,6 @@ class SettingsPage extends StatelessWidget {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
bottom: const PreferredSize(
|
|
||||||
preferredSize: Size.fromHeight(1),
|
|
||||||
child: Divider(height: 1),
|
|
||||||
),
|
|
||||||
title: const Text('setting_pages_app_bar_settings').tr(),
|
title: const Text('setting_pages_app_bar_settings').tr(),
|
||||||
),
|
),
|
||||||
body: context.isMobile ? _MobileLayout() : _TabletLayout(),
|
body: context.isMobile ? _MobileLayout() : _TabletLayout(),
|
||||||
|
@ -67,13 +63,18 @@ class _MobileLayout extends StatelessWidget {
|
||||||
children: SettingSection.values
|
children: SettingSection.values
|
||||||
.map(
|
.map(
|
||||||
(s) => ListTile(
|
(s) => ListTile(
|
||||||
title: Text(
|
contentPadding:
|
||||||
s.title,
|
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 16.0),
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
).tr(),
|
|
||||||
leading: Icon(s.icon),
|
leading: Icon(s.icon),
|
||||||
|
title: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Text(
|
||||||
|
s.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
onTap: () => context.pushRoute(SettingsSubRoute(section: s)),
|
onTap: () => context.pushRoute(SettingsSubRoute(section: s)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -102,7 +103,7 @@ class _TabletLayout extends HookWidget {
|
||||||
leading: Icon(s.icon),
|
leading: Icon(s.icon),
|
||||||
selected: s.index == selectedSection.value.index,
|
selected: s.index == selectedSection.value.index,
|
||||||
selectedColor: context.primaryColor,
|
selectedColor: context.primaryColor,
|
||||||
selectedTileColor: context.primaryColor.withAlpha(50),
|
selectedTileColor: context.themeData.highlightColor,
|
||||||
onTap: () => selectedSection.value = s,
|
onTap: () => selectedSection.value = s,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -20,7 +20,6 @@ class LibraryPage extends HookConsumerWidget {
|
||||||
final trashEnabled =
|
final trashEnabled =
|
||||||
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||||
final albums = ref.watch(albumProvider);
|
final albums = ref.watch(albumProvider);
|
||||||
final isDarkTheme = context.isDarkTheme;
|
|
||||||
final albumSortOption = ref.watch(albumSortByOptionsProvider);
|
final albumSortOption = ref.watch(albumSortByOptionsProvider);
|
||||||
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
|
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
|
||||||
|
|
||||||
|
@ -116,12 +115,7 @@ class LibraryPage extends HookConsumerWidget {
|
||||||
width: cardSize,
|
width: cardSize,
|
||||||
height: cardSize,
|
height: cardSize,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
color: context.colorScheme.surfaceContainer,
|
||||||
color: isDarkTheme
|
|
||||||
? const Color.fromARGB(255, 53, 53, 53)
|
|
||||||
: const Color.fromARGB(255, 203, 203, 203),
|
|
||||||
),
|
|
||||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
|
@ -139,7 +133,9 @@ class LibraryPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'library_page_new_album',
|
'library_page_new_album',
|
||||||
style: context.textTheme.labelLarge,
|
style: context.textTheme.labelLarge?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -156,26 +152,25 @@ class LibraryPage extends HookConsumerWidget {
|
||||||
Function() onClick,
|
Function() onClick,
|
||||||
) {
|
) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: OutlinedButton.icon(
|
child: FilledButton.icon(
|
||||||
onPressed: onClick,
|
onPressed: onClick,
|
||||||
label: Padding(
|
label: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.onSurface,
|
||||||
? Colors.white
|
|
||||||
: Colors.black.withAlpha(200),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: OutlinedButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
elevation: 0,
|
||||||
backgroundColor: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
||||||
side: BorderSide(
|
backgroundColor: context.colorScheme.surfaceContainer,
|
||||||
color: isDarkTheme ? Colors.grey[800]! : Colors.grey[300]!,
|
|
||||||
),
|
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
icon,
|
icon,
|
||||||
|
@ -247,6 +242,7 @@ class LibraryPage extends HookConsumerWidget {
|
||||||
Text(
|
Text(
|
||||||
'library_page_albums',
|
'library_page_albums',
|
||||||
style: context.textTheme.bodyLarge?.copyWith(
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/widgets/forms/login/login_form.dart';
|
import 'package:immich_mobile/widgets/forms/login/login_form.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
@ -39,8 +40,8 @@ class LoginPage extends HookConsumerWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'v${appVersion.value}',
|
'v${appVersion.value}',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey,
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontFamily: "Inconsolata",
|
fontFamily: "Inconsolata",
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.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/models/search/search_curated_content.model.dart';
|
import 'package:immich_mobile/models/search/search_curated_content.model.dart';
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
import 'package:immich_mobile/providers/search/people.provider.dart';
|
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||||
|
@ -38,7 +39,7 @@ class SearchPage extends HookConsumerWidget {
|
||||||
fontSize: 15.0,
|
fontSize: 15.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
Color categoryIconColor = context.isDarkTheme ? Colors.white : Colors.black;
|
Color categoryIconColor = context.colorScheme.onSurface;
|
||||||
|
|
||||||
showNameEditModel(
|
showNameEditModel(
|
||||||
String personId,
|
String personId,
|
||||||
|
@ -128,13 +129,9 @@ class SearchPage extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
color: context.colorScheme.surfaceContainerHigh,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(50),
|
||||||
side: BorderSide(
|
|
||||||
color: context.isDarkTheme
|
|
||||||
? Colors.grey[800]!
|
|
||||||
: const Color.fromARGB(255, 225, 225, 225),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -144,13 +141,15 @@ class SearchPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.search, color: context.primaryColor),
|
Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
const SizedBox(width: 16.0),
|
const SizedBox(width: 16.0),
|
||||||
Text(
|
Text(
|
||||||
"search_bar_hint",
|
"search_bar_hint",
|
||||||
style: context.textTheme.bodyLarge?.copyWith(
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
color:
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
context.isDarkTheme ? Colors.white70 : Colors.black54,
|
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.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/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
|
import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
|
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
|
||||||
|
@ -509,7 +510,7 @@ class SearchInputPage extends HookConsumerWidget {
|
||||||
? 'contextual_search'.tr()
|
? 'contextual_search'.tr()
|
||||||
: 'filename_search'.tr(),
|
: 'filename_search'.tr(),
|
||||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
||||||
color: context.themeData.colorScheme.onSurface.withOpacity(0.75),
|
color: context.themeData.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
enabledBorder: const UnderlineInputBorder(
|
enabledBorder: const UnderlineInputBorder(
|
||||||
|
|
|
@ -30,6 +30,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
const padding = 20.0;
|
const padding = 20.0;
|
||||||
final themeData = context.themeData;
|
final themeData = context.themeData;
|
||||||
|
final colorScheme = context.colorScheme;
|
||||||
final descriptionController =
|
final descriptionController =
|
||||||
useTextEditingController(text: existingLink?.description ?? "");
|
useTextEditingController(text: existingLink?.description ?? "");
|
||||||
final descriptionFocusNode = useFocusNode();
|
final descriptionFocusNode = useFocusNode();
|
||||||
|
@ -58,7 +59,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
Text(
|
Text(
|
||||||
existingLink!.title,
|
existingLink!.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -81,7 +82,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
existingLink!.description ?? "--",
|
existingLink!.description ?? "--",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -109,7 +110,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
labelText: 'shared_link_edit_description'.tr(),
|
labelText: 'shared_link_edit_description'.tr(),
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
|
@ -135,7 +136,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
labelText: 'shared_link_edit_password'.tr(),
|
labelText: 'shared_link_edit_password'.tr(),
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
|
@ -157,7 +158,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
onChanged: newShareLink.value.isEmpty
|
onChanged: newShareLink.value.isEmpty
|
||||||
? (value) => showMetadata.value = value
|
? (value) => showMetadata.value = value
|
||||||
: null,
|
: null,
|
||||||
activeColor: themeData.primaryColor,
|
activeColor: colorScheme.primary,
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
"shared_link_edit_show_meta",
|
"shared_link_edit_show_meta",
|
||||||
|
@ -173,7 +174,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
onChanged: newShareLink.value.isEmpty
|
onChanged: newShareLink.value.isEmpty
|
||||||
? (value) => allowDownload.value = value
|
? (value) => allowDownload.value = value
|
||||||
: null,
|
: null,
|
||||||
activeColor: themeData.primaryColor,
|
activeColor: colorScheme.primary,
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
"shared_link_edit_allow_download",
|
"shared_link_edit_allow_download",
|
||||||
|
@ -189,7 +190,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
onChanged: newShareLink.value.isEmpty
|
onChanged: newShareLink.value.isEmpty
|
||||||
? (value) => allowUpload.value = value
|
? (value) => allowUpload.value = value
|
||||||
: null,
|
: null,
|
||||||
activeColor: themeData.primaryColor,
|
activeColor: colorScheme.primary,
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
"shared_link_edit_allow_upload",
|
"shared_link_edit_allow_upload",
|
||||||
|
@ -205,7 +206,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
onChanged: newShareLink.value.isEmpty
|
onChanged: newShareLink.value.isEmpty
|
||||||
? (value) => editExpiry.value = value
|
? (value) => editExpiry.value = value
|
||||||
: null,
|
: null,
|
||||||
activeColor: themeData.primaryColor,
|
activeColor: colorScheme.primary,
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
"shared_link_edit_change_expiry",
|
"shared_link_edit_change_expiry",
|
||||||
|
@ -221,7 +222,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
||||||
"shared_link_edit_expire_after",
|
"shared_link_edit_expire_after",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
enableSearch: false,
|
enableSearch: false,
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||||
|
@ -83,20 +84,24 @@ class SharingPage extends HookConsumerWidget {
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: context.primaryColor,
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: isOwner
|
subtitle: isOwner
|
||||||
? Text(
|
? Text(
|
||||||
'album_thumbnail_owned'.tr(),
|
'album_thumbnail_owned'.tr(),
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: album.ownerName != null
|
: album.ownerName != null
|
||||||
? Text(
|
? Text(
|
||||||
'album_thumbnail_shared_by'
|
'album_thumbnail_shared_by'
|
||||||
.tr(args: [album.ownerName!]),
|
.tr(args: [album.ownerName!]),
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
onTap: () => context
|
onTap: () => context
|
||||||
|
@ -166,11 +171,13 @@ class SharingPage extends HookConsumerWidget {
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: Colors.grey,
|
color: context.isDarkTheme
|
||||||
width: 0.5,
|
? const Color(0xFF383838)
|
||||||
|
: Colors.black12,
|
||||||
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
|
||||||
enum AppSettingsEnum<T> {
|
enum AppSettingsEnum<T> {
|
||||||
|
@ -8,6 +9,21 @@ enum AppSettingsEnum<T> {
|
||||||
"themeMode",
|
"themeMode",
|
||||||
"system",
|
"system",
|
||||||
), // "light","dark","system"
|
), // "light","dark","system"
|
||||||
|
primaryColor<String>(
|
||||||
|
StoreKey.primaryColor,
|
||||||
|
"primaryColor",
|
||||||
|
defaultColorPresetName,
|
||||||
|
),
|
||||||
|
dynamicTheme<bool>(
|
||||||
|
StoreKey.dynamicTheme,
|
||||||
|
"dynamicTheme",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
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),
|
||||||
groupAssetsBy<int>(StoreKey.groupAssetsBy, "groupBy", 0),
|
groupAssetsBy<int>(StoreKey.groupAssetsBy, "groupBy", 0),
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
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:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
|
|
||||||
final immichThemeProvider = StateProvider<ThemeMode>((ref) {
|
class ImmichTheme {
|
||||||
|
ColorScheme light;
|
||||||
|
ColorScheme dark;
|
||||||
|
|
||||||
|
ImmichTheme({required this.light, required this.dark});
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmichTheme? _immichDynamicTheme;
|
||||||
|
bool get isDynamicThemeAvailable => _immichDynamicTheme != null;
|
||||||
|
|
||||||
|
final immichThemeModeProvider = StateProvider<ThemeMode>((ref) {
|
||||||
var themeMode = ref
|
var themeMode = ref
|
||||||
.watch(appSettingsServiceProvider)
|
.watch(appSettingsServiceProvider)
|
||||||
.getSetting(AppSettingsEnum.themeMode);
|
.getSetting(AppSettingsEnum.themeMode);
|
||||||
|
@ -20,266 +32,241 @@ final immichThemeProvider = StateProvider<ThemeMode>((ref) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final ThemeData base = ThemeData(
|
final immichThemePresetProvider = StateProvider<ImmichColorPreset>((ref) {
|
||||||
chipTheme: const ChipThemeData(
|
var appSettingsProvider = ref.watch(appSettingsServiceProvider);
|
||||||
side: BorderSide.none,
|
var primaryColorName =
|
||||||
),
|
appSettingsProvider.getSetting(AppSettingsEnum.primaryColor);
|
||||||
sliderTheme: const SliderThemeData(
|
|
||||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
|
||||||
trackHeight: 2.0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ThemeData immichLightTheme = ThemeData(
|
debugPrint("Current theme preset $primaryColorName");
|
||||||
useMaterial3: true,
|
|
||||||
brightness: Brightness.light,
|
|
||||||
colorScheme: ColorScheme.fromSeed(
|
|
||||||
seedColor: Colors.indigo,
|
|
||||||
),
|
|
||||||
primarySwatch: Colors.indigo,
|
|
||||||
primaryColor: Colors.indigo,
|
|
||||||
hintColor: Colors.indigo,
|
|
||||||
focusColor: Colors.indigo,
|
|
||||||
splashColor: Colors.indigo.withOpacity(0.15),
|
|
||||||
fontFamily: 'Overpass',
|
|
||||||
scaffoldBackgroundColor: immichBackgroundColor,
|
|
||||||
snackBarTheme: const SnackBarThemeData(
|
|
||||||
contentTextStyle: TextStyle(
|
|
||||||
fontFamily: 'Overpass',
|
|
||||||
color: Colors.indigo,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
appBarTheme: const AppBarTheme(
|
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
fontFamily: 'Overpass',
|
|
||||||
color: Colors.indigo,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
backgroundColor: immichBackgroundColor,
|
|
||||||
foregroundColor: Colors.indigo,
|
|
||||||
elevation: 0,
|
|
||||||
scrolledUnderElevation: 0,
|
|
||||||
centerTitle: true,
|
|
||||||
),
|
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
|
||||||
type: BottomNavigationBarType.fixed,
|
|
||||||
backgroundColor: immichBackgroundColor,
|
|
||||||
selectedItemColor: Colors.indigo,
|
|
||||||
),
|
|
||||||
cardTheme: const CardTheme(
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
),
|
|
||||||
drawerTheme: const DrawerThemeData(
|
|
||||||
backgroundColor: immichBackgroundColor,
|
|
||||||
),
|
|
||||||
textTheme: const TextTheme(
|
|
||||||
displayLarge: TextStyle(
|
|
||||||
fontSize: 26,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.indigo,
|
|
||||||
),
|
|
||||||
displayMedium: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
displaySmall: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.indigo,
|
|
||||||
),
|
|
||||||
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: Colors.indigo,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
chipTheme: base.chipTheme,
|
|
||||||
sliderTheme: base.sliderTheme,
|
|
||||||
popupMenuTheme: const PopupMenuThemeData(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
|
||||||
),
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
|
||||||
indicatorColor: Colors.indigo.withOpacity(0.15),
|
|
||||||
iconTheme: WidgetStatePropertyAll(
|
|
||||||
IconThemeData(color: Colors.grey[700]),
|
|
||||||
),
|
|
||||||
backgroundColor: immichBackgroundColor,
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
labelTextStyle: WidgetStatePropertyAll(
|
|
||||||
TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.grey[800],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
dialogTheme: const DialogTheme(
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
),
|
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Colors.indigo,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
color: Colors.indigo,
|
|
||||||
),
|
|
||||||
hintStyle: TextStyle(
|
|
||||||
fontSize: 14.0,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textSelectionTheme: const TextSelectionThemeData(
|
|
||||||
cursorColor: Colors.indigo,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ThemeData immichDarkTheme = ThemeData(
|
try {
|
||||||
useMaterial3: true,
|
return ImmichColorPreset.values
|
||||||
brightness: Brightness.dark,
|
.firstWhere((e) => e.name == primaryColorName);
|
||||||
primarySwatch: Colors.indigo,
|
} catch (e) {
|
||||||
primaryColor: immichDarkThemePrimaryColor,
|
debugPrint(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
"Theme preset $primaryColorName not found. Applying default preset.",
|
||||||
seedColor: immichDarkThemePrimaryColor,
|
);
|
||||||
brightness: Brightness.dark,
|
appSettingsProvider.setSetting(
|
||||||
),
|
AppSettingsEnum.primaryColor,
|
||||||
scaffoldBackgroundColor: immichDarkBackgroundColor,
|
defaultColorPresetName,
|
||||||
hintColor: Colors.grey[600],
|
);
|
||||||
fontFamily: 'Overpass',
|
return defaultColorPreset;
|
||||||
snackBarTheme: SnackBarThemeData(
|
}
|
||||||
contentTextStyle: const TextStyle(
|
});
|
||||||
fontFamily: 'Overpass',
|
|
||||||
color: immichDarkThemePrimaryColor,
|
final dynamicThemeSettingProvider = StateProvider<bool>((ref) {
|
||||||
fontWeight: FontWeight.bold,
|
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,
|
||||||
|
),
|
||||||
|
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({
|
||||||
|
required ImmichTheme theme,
|
||||||
|
}) {
|
||||||
|
return ImmichTheme(
|
||||||
|
light: theme.light.copyWith(
|
||||||
|
surface: const Color(0xFFf9f9f9),
|
||||||
|
onSurface: const Color(0xFF1b1b1b),
|
||||||
|
surfaceContainerLowest: const Color(0xFFffffff),
|
||||||
|
surfaceContainerLow: const Color(0xFFf3f3f3),
|
||||||
|
surfaceContainer: const Color(0xFFeeeeee),
|
||||||
|
surfaceContainerHigh: const Color(0xFFe8e8e8),
|
||||||
|
surfaceContainerHighest: const Color(0xFFe2e2e2),
|
||||||
|
surfaceDim: const Color(0xFFdadada),
|
||||||
|
surfaceBright: const Color(0xFFf9f9f9),
|
||||||
|
onSurfaceVariant: const Color(0xFF4c4546),
|
||||||
|
inverseSurface: const Color(0xFF303030),
|
||||||
|
onInverseSurface: const Color(0xFFf1f1f1),
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.grey[900],
|
dark: theme.dark.copyWith(
|
||||||
),
|
surface: const Color(0xFF131313),
|
||||||
textButtonTheme: TextButtonThemeData(
|
onSurface: const Color(0xFFE2E2E2),
|
||||||
style: TextButton.styleFrom(
|
surfaceContainerLowest: const Color(0xFF0E0E0E),
|
||||||
foregroundColor: immichDarkThemePrimaryColor,
|
surfaceContainerLow: const Color(0xFF1B1B1B),
|
||||||
|
surfaceContainer: const Color(0xFF1F1F1F),
|
||||||
|
surfaceContainerHigh: const Color(0xFF242424),
|
||||||
|
surfaceContainerHighest: const Color(0xFF2E2E2E),
|
||||||
|
surfaceDim: const Color(0xFF131313),
|
||||||
|
surfaceBright: const Color(0xFF353535),
|
||||||
|
onSurfaceVariant: const Color(0xFFCfC4C5),
|
||||||
|
inverseSurface: const Color(0xFFE2E2E2),
|
||||||
|
onInverseSurface: const Color(0xFF303030),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
appBarTheme: const AppBarTheme(
|
}
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
fontFamily: 'Overpass',
|
ThemeData getThemeData({required ColorScheme colorScheme}) {
|
||||||
color: immichDarkThemePrimaryColor,
|
var isDark = colorScheme.brightness == Brightness.dark;
|
||||||
fontWeight: FontWeight.bold,
|
var primaryColor = colorScheme.primary;
|
||||||
fontSize: 18,
|
|
||||||
|
return ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: isDark ? Brightness.dark : Brightness.light,
|
||||||
|
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,
|
||||||
),
|
),
|
||||||
backgroundColor: Color.fromARGB(255, 32, 33, 35),
|
fontFamily: 'Overpass',
|
||||||
foregroundColor: immichDarkThemePrimaryColor,
|
snackBarTheme: SnackBarThemeData(
|
||||||
elevation: 0,
|
contentTextStyle: TextStyle(
|
||||||
scrolledUnderElevation: 0,
|
fontFamily: 'Overpass',
|
||||||
centerTitle: true,
|
color: primaryColor,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
),
|
||||||
type: BottomNavigationBarType.fixed,
|
backgroundColor: colorScheme.surfaceContainerHighest,
|
||||||
backgroundColor: Color.fromARGB(255, 35, 36, 37),
|
|
||||||
selectedItemColor: immichDarkThemePrimaryColor,
|
|
||||||
),
|
|
||||||
drawerTheme: DrawerThemeData(
|
|
||||||
backgroundColor: immichDarkBackgroundColor,
|
|
||||||
scrimColor: Colors.white.withOpacity(0.1),
|
|
||||||
),
|
|
||||||
textTheme: const TextTheme(
|
|
||||||
displayLarge: TextStyle(
|
|
||||||
fontSize: 26,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color.fromARGB(255, 255, 255, 255),
|
|
||||||
),
|
),
|
||||||
displayMedium: TextStyle(
|
appBarTheme: AppBarTheme(
|
||||||
fontSize: 14,
|
titleTextStyle: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: primaryColor,
|
||||||
color: Color.fromARGB(255, 255, 255, 255),
|
fontFamily: 'Overpass',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
backgroundColor:
|
||||||
|
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
||||||
|
foregroundColor: primaryColor,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
displaySmall: TextStyle(
|
textTheme: TextTheme(
|
||||||
fontSize: 12,
|
displayLarge: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 26,
|
||||||
color: immichDarkThemePrimaryColor,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
color: isDark ? Colors.white : primaryColor,
|
||||||
titleSmall: TextStyle(
|
),
|
||||||
fontSize: 16.0,
|
displayMedium: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
titleMedium: TextStyle(
|
color: isDark ? Colors.white : Colors.black87,
|
||||||
fontSize: 18.0,
|
),
|
||||||
fontWeight: FontWeight.bold,
|
displaySmall: TextStyle(
|
||||||
),
|
fontSize: 12,
|
||||||
titleLarge: TextStyle(
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 26.0,
|
color: primaryColor,
|
||||||
fontWeight: FontWeight.bold,
|
),
|
||||||
),
|
titleSmall: const TextStyle(
|
||||||
),
|
fontSize: 16.0,
|
||||||
cardColor: Colors.grey[900],
|
fontWeight: FontWeight.bold,
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
),
|
||||||
style: ElevatedButton.styleFrom(
|
titleMedium: const TextStyle(
|
||||||
foregroundColor: Colors.black87,
|
fontSize: 18.0,
|
||||||
backgroundColor: immichDarkThemePrimaryColor,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
titleLarge: const TextStyle(
|
||||||
chipTheme: base.chipTheme,
|
fontSize: 26.0,
|
||||||
sliderTheme: base.sliderTheme,
|
fontWeight: FontWeight.bold,
|
||||||
popupMenuTheme: const PopupMenuThemeData(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
|
||||||
),
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
),
|
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
|
||||||
indicatorColor: immichDarkThemePrimaryColor.withOpacity(0.4),
|
|
||||||
iconTheme: WidgetStatePropertyAll(
|
|
||||||
IconThemeData(color: Colors.grey[500]),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.grey[900],
|
|
||||||
surfaceTintColor: Colors.transparent,
|
|
||||||
labelTextStyle: WidgetStatePropertyAll(
|
|
||||||
TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.grey[300],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
dialogTheme: const DialogTheme(
|
style: ElevatedButton.styleFrom(
|
||||||
surfaceTintColor: Colors.transparent,
|
backgroundColor: primaryColor,
|
||||||
),
|
foregroundColor: isDark ? Colors.black87 : Colors.white,
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: immichDarkThemePrimaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
labelStyle: TextStyle(
|
chipTheme: const ChipThemeData(
|
||||||
color: immichDarkThemePrimaryColor,
|
side: BorderSide.none,
|
||||||
),
|
),
|
||||||
hintStyle: TextStyle(
|
sliderTheme: const SliderThemeData(
|
||||||
fontSize: 14.0,
|
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||||
fontWeight: FontWeight.normal,
|
trackHeight: 2.0,
|
||||||
),
|
),
|
||||||
),
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
textSelectionTheme: const TextSelectionThemeData(
|
type: BottomNavigationBarType.fixed,
|
||||||
cursorColor: immichDarkThemePrimaryColor,
|
),
|
||||||
),
|
popupMenuTheme: const PopupMenuThemeData(
|
||||||
);
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
|
backgroundColor:
|
||||||
|
isDark ? colorScheme.surfaceContainer : colorScheme.surface,
|
||||||
|
labelTextStyle: const WidgetStatePropertyAll(
|
||||||
|
TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: colorScheme.outlineVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
hintStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textSelectionTheme: TextSelectionThemeData(
|
||||||
|
cursorColor: primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class AlbumActionOutlinedButton extends StatelessWidget {
|
class AlbumActionFilledButton extends StatelessWidget {
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String labelText;
|
final String labelText;
|
||||||
final IconData iconData;
|
final IconData iconData;
|
||||||
|
|
||||||
const AlbumActionOutlinedButton({
|
const AlbumActionFilledButton({
|
||||||
super.key,
|
super.key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
required this.labelText,
|
required this.labelText,
|
||||||
|
@ -17,18 +17,13 @@ class AlbumActionOutlinedButton extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 16.0),
|
padding: const EdgeInsets.only(right: 16.0),
|
||||||
child: OutlinedButton.icon(
|
child: FilledButton.icon(
|
||||||
style: OutlinedButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
),
|
),
|
||||||
side: BorderSide(
|
backgroundColor: context.colorScheme.surfaceContainerHigh,
|
||||||
width: 1,
|
|
||||||
color: context.isDarkTheme
|
|
||||||
? const Color.fromARGB(255, 63, 63, 63)
|
|
||||||
: const Color.fromARGB(255, 206, 206, 206),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
iconData,
|
iconData,
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||||
|
|
||||||
class AlbumThumbnailCard extends StatelessWidget {
|
class AlbumThumbnailCard extends StatelessWidget {
|
||||||
|
@ -23,8 +24,6 @@ class AlbumThumbnailCard extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var isDarkTheme = context.isDarkTheme;
|
|
||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
var cardSize = constraints.maxWidth;
|
var cardSize = constraints.maxWidth;
|
||||||
|
@ -34,12 +33,13 @@ class AlbumThumbnailCard extends StatelessWidget {
|
||||||
height: cardSize,
|
height: cardSize,
|
||||||
width: cardSize,
|
width: cardSize,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isDarkTheme ? Colors.grey[800] : Colors.grey[200],
|
color: context.colorScheme.surfaceContainerHigh,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.no_photography,
|
Icons.no_photography,
|
||||||
size: cardSize * .15,
|
size: cardSize * .15,
|
||||||
|
color: context.colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -65,6 +65,9 @@ class AlbumThumbnailCard extends StatelessWidget {
|
||||||
return RichText(
|
return RichText(
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: album.assetCount == 1
|
text: album.assetCount == 1
|
||||||
|
@ -72,14 +75,9 @@ class AlbumThumbnailCard extends StatelessWidget {
|
||||||
.tr(args: ['${album.assetCount}'])
|
.tr(args: ['${album.assetCount}'])
|
||||||
: 'album_thumbnail_card_items'
|
: 'album_thumbnail_card_items'
|
||||||
.tr(args: ['${album.assetCount}']),
|
.tr(args: ['${album.assetCount}']),
|
||||||
style: context.textTheme.bodyMedium,
|
|
||||||
),
|
),
|
||||||
if (owner != null) const TextSpan(text: ' · '),
|
if (owner != null) const TextSpan(text: ' · '),
|
||||||
if (owner != null)
|
if (owner != null) TextSpan(text: owner),
|
||||||
TextSpan(
|
|
||||||
text: owner,
|
|
||||||
style: context.textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -112,7 +110,7 @@ class AlbumThumbnailCard extends StatelessWidget {
|
||||||
album.name,
|
album.name,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: context.primaryColor,
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -20,8 +20,6 @@ class AlbumTitleTextField extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isDarkTheme = context.isDarkTheme;
|
|
||||||
|
|
||||||
return TextField(
|
return TextField(
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
if (v.isEmpty) {
|
if (v.isEmpty) {
|
||||||
|
@ -35,7 +33,7 @@ class AlbumTitleTextField extends ConsumerWidget {
|
||||||
focusNode: albumTitleTextFieldFocusNode,
|
focusNode: albumTitleTextFieldFocusNode,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
controller: albumTitleController,
|
controller: albumTitleController,
|
||||||
|
@ -61,24 +59,18 @@ class AlbumTitleTextField extends ConsumerWidget {
|
||||||
splashRadius: 10,
|
splashRadius: 10,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: const OutlineInputBorder(
|
||||||
borderSide: const BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: const OutlineInputBorder(
|
||||||
borderSide: const BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
),
|
||||||
hintText: 'share_add_title'.tr(),
|
hintText: 'share_add_title'.tr(),
|
||||||
hintStyle: TextStyle(
|
hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
focusColor: Colors.grey[300],
|
focusColor: Colors.grey[300],
|
||||||
fillColor: isDarkTheme
|
fillColor: context.scaffoldBackgroundColor,
|
||||||
? const Color.fromARGB(255, 32, 33, 35)
|
|
||||||
: Colors.grey[200],
|
|
||||||
filled: isAlbumTitleTextFieldFocus.value,
|
filled: isAlbumTitleTextFieldFocus.value,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -95,7 +95,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||||
'action_common_confirm',
|
'action_common_confirm',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: !context.isDarkTheme ? Colors.red : Colors.red[300],
|
color: context.colorScheme.error,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -73,24 +73,18 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||||
splashRadius: 10,
|
splashRadius: 10,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: const OutlineInputBorder(
|
||||||
borderSide: const BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: const OutlineInputBorder(
|
||||||
borderSide: const BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
),
|
||||||
focusColor: Colors.grey[300],
|
focusColor: Colors.grey[300],
|
||||||
fillColor: context.isDarkTheme
|
fillColor: context.scaffoldBackgroundColor,
|
||||||
? const Color.fromARGB(255, 32, 33, 35)
|
|
||||||
: Colors.grey[200],
|
|
||||||
filled: titleFocusNode.hasFocus,
|
filled: titleFocusNode.hasFocus,
|
||||||
hintText: 'share_add_title'.tr(),
|
hintText: 'share_add_title'.tr(),
|
||||||
hintStyle: TextStyle(
|
hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -281,7 +281,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||||
ScrollController scrollController,
|
ScrollController scrollController,
|
||||||
) {
|
) {
|
||||||
return Card(
|
return Card(
|
||||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
|
color: context.colorScheme.surfaceContainerLow,
|
||||||
surfaceTintColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
elevation: 18.0,
|
elevation: 18.0,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|
|
@ -22,12 +22,15 @@ class DisableMultiSelectButton extends ConsumerWidget {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () => onPressed(),
|
onPressed: () => onPressed(),
|
||||||
icon: const Icon(Icons.close_rounded),
|
icon: Icon(
|
||||||
|
Icons.close_rounded,
|
||||||
|
color: context.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
'$selectedItemCount',
|
'$selectedItemCount',
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
height: 2.5,
|
height: 2.5,
|
||||||
color: context.isDarkTheme ? Colors.black : Colors.white,
|
color: context.colorScheme.onPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
|
@ -74,9 +75,9 @@ class GroupDividerTitle extends HookConsumerWidget {
|
||||||
Icons.check_circle_rounded,
|
Icons.check_circle_rounded,
|
||||||
color: context.primaryColor,
|
color: context.primaryColor,
|
||||||
)
|
)
|
||||||
: const Icon(
|
: Icon(
|
||||||
Icons.check_circle_outline_rounded,
|
Icons.check_circle_outline_rounded,
|
||||||
color: Colors.grey,
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:flutter/services.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/extensions/collection_extensions.dart';
|
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_drag_region.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_drag_region.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart';
|
import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart';
|
||||||
|
@ -266,7 +267,9 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
|
||||||
scrollStateListener: dragScrolling,
|
scrollStateListener: dragScrolling,
|
||||||
itemPositionsListener: _itemPositionsListener,
|
itemPositionsListener: _itemPositionsListener,
|
||||||
controller: _itemScrollController,
|
controller: _itemScrollController,
|
||||||
backgroundColor: context.themeData.hintColor,
|
backgroundColor: context.isDarkTheme
|
||||||
|
? context.colorScheme.primary.darken(amount: .5)
|
||||||
|
: context.colorScheme.primary,
|
||||||
labelTextBuilder: _labelBuilder,
|
labelTextBuilder: _labelBuilder,
|
||||||
padding: appBarOffset()
|
padding: appBarOffset()
|
||||||
? const EdgeInsets.only(top: 60)
|
? const EdgeInsets.only(top: 60)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.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/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||||
import 'package:immich_mobile/utils/storage_indicator.dart';
|
import 'package:immich_mobile/utils/storage_indicator.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -42,8 +43,8 @@ class ThumbnailImage extends ConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final assetContainerColor = context.isDarkTheme
|
final assetContainerColor = context.isDarkTheme
|
||||||
? Colors.blueGrey
|
? context.primaryColor.darken(amount: 0.6)
|
||||||
: context.themeData.primaryColorLight;
|
: context.primaryColor.lighten(amount: 0.8);
|
||||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||||
final isFromDto = asset.id == Isar.autoIncrement;
|
final isFromDto = asset.id == Isar.autoIncrement;
|
||||||
|
|
||||||
|
@ -192,8 +193,8 @@ class ThumbnailImage extends ConsumerWidget {
|
||||||
bottom: 5,
|
bottom: 5,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
storageIcon(asset),
|
storageIcon(asset),
|
||||||
color: Colors.white,
|
color: Colors.white.withOpacity(.8),
|
||||||
size: 18,
|
size: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (asset.isFavorite)
|
if (asset.isFavorite)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
class ThumbnailPlaceholder extends StatelessWidget {
|
class ThumbnailPlaceholder extends StatelessWidget {
|
||||||
final EdgeInsets margin;
|
final EdgeInsets margin;
|
||||||
|
@ -13,25 +14,20 @@ class ThumbnailPlaceholder extends StatelessWidget {
|
||||||
this.height = 250,
|
this.height = 250,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const _brightColors = [
|
|
||||||
Color(0xFFF1F3F4),
|
|
||||||
Color(0xFFB4B6B8),
|
|
||||||
];
|
|
||||||
|
|
||||||
static const _darkColors = [
|
|
||||||
Color(0xFF3B3F42),
|
|
||||||
Color(0xFF2B2F32),
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var gradientColors = [
|
||||||
|
context.colorScheme.surfaceContainer,
|
||||||
|
context.colorScheme.surfaceContainer.darken(amount: .1),
|
||||||
|
];
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
margin: margin,
|
margin: margin,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: context.isDarkTheme ? _darkColors : _brightColors,
|
colors: gradientColors,
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/asset_description.service.dart';
|
import 'package:immich_mobile/services/asset_description.service.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
@ -23,7 +24,6 @@ class DescriptionInput extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
|
||||||
final controller = useTextEditingController();
|
final controller = useTextEditingController();
|
||||||
final focusNode = useFocusNode();
|
final focusNode = useFocusNode();
|
||||||
final isFocus = useState(false);
|
final isFocus = useState(false);
|
||||||
|
@ -71,7 +71,7 @@ class DescriptionInput extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.cancel_rounded,
|
Icons.cancel_rounded,
|
||||||
color: Colors.grey[500],
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
splashRadius: 10,
|
splashRadius: 10,
|
||||||
);
|
);
|
||||||
|
@ -100,9 +100,6 @@ class DescriptionInput extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'description_input_hint_text'.tr(),
|
hintText: 'description_input_hint_text'.tr(),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintStyle: context.textTheme.labelLarge?.copyWith(
|
|
||||||
color: textColor.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
suffixIcon: suffixIcon,
|
suffixIcon: suffixIcon,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final assetWithExif = ref.watch(assetDetailProvider(asset));
|
final assetWithExif = ref.watch(assetDetailProvider(asset));
|
||||||
var textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
var textColor = context.colorScheme.onSurface;
|
||||||
final ExifInfo? exifInfo = (assetWithExif.value ?? asset).exifInfo;
|
final ExifInfo? exifInfo = (assetWithExif.value ?? asset).exifInfo;
|
||||||
// Format the date time with the timezone
|
// Format the date time with the timezone
|
||||||
final (dt, timeZone) =
|
final (dt, timeZone) =
|
||||||
|
|
|
@ -178,6 +178,7 @@ class TopControlAppBar extends HookConsumerWidget {
|
||||||
actionsIconTheme: const IconThemeData(
|
actionsIconTheme: const IconThemeData(
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
),
|
),
|
||||||
|
shape: const Border(),
|
||||||
actions: [
|
actions: [
|
||||||
if (asset.isRemote && isOwner) buildFavoriteButton(a),
|
if (asset.isRemote && isOwner) buildFavoriteButton(a),
|
||||||
if (asset.livePhotoVideoId != null) buildLivePhotoButton(),
|
if (asset.livePhotoVideoId != null) buildLivePhotoButton(),
|
||||||
|
|
|
@ -47,22 +47,22 @@ class AlbumInfoListTile extends HookConsumerWidget {
|
||||||
|
|
||||||
buildIcon() {
|
buildIcon() {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return const Icon(
|
return Icon(
|
||||||
Icons.check_circle_rounded,
|
Icons.check_circle_rounded,
|
||||||
color: Colors.green,
|
color: context.colorScheme.primary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExcluded) {
|
if (isExcluded) {
|
||||||
return const Icon(
|
return Icon(
|
||||||
Icons.remove_circle_rounded,
|
Icons.remove_circle_rounded,
|
||||||
color: Colors.red,
|
color: context.colorScheme.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Icon(
|
return Icon(
|
||||||
Icons.circle,
|
Icons.circle,
|
||||||
color: context.isDarkTheme ? Colors.grey[400] : Colors.black45,
|
color: context.colorScheme.surfaceContainerHighest,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +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:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
|
||||||
class BackupInfoCard extends StatelessWidget {
|
class BackupInfoCard extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -19,9 +20,7 @@ class BackupInfoCard extends StatelessWidget {
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20), // if you need this
|
borderRadius: BorderRadius.circular(20), // if you need this
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.outlineVariant,
|
||||||
? const Color.fromARGB(255, 56, 56, 56)
|
|
||||||
: Colors.black12,
|
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -38,7 +37,9 @@ class BackupInfoCard extends StatelessWidget {
|
||||||
padding: const EdgeInsets.only(top: 4.0, right: 18.0),
|
padding: const EdgeInsets.only(top: 4.0, right: 18.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
subtitle,
|
subtitle,
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: Column(
|
trailing: Column(
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
|
||||||
|
@ -82,22 +83,20 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||||
Widget buildAssetInfoTable() {
|
Widget buildAssetInfoTable() {
|
||||||
return Table(
|
return Table(
|
||||||
border: TableBorder.all(
|
border: TableBorder.all(
|
||||||
color: context.themeData.primaryColorLight,
|
color: context.colorScheme.outlineVariant,
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TableRow(
|
TableRow(
|
||||||
decoration: const BoxDecoration(
|
|
||||||
// color: Colors.grey[100],
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
TableCell(
|
TableCell(
|
||||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(6.0),
|
padding: const EdgeInsets.all(6.0),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'backup_controller_page_filename',
|
'backup_controller_page_filename',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 10.0,
|
fontSize: 10.0,
|
||||||
),
|
),
|
||||||
|
@ -109,17 +108,15 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
decoration: const BoxDecoration(
|
|
||||||
// color: Colors.grey[200],
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
TableCell(
|
TableCell(
|
||||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(6.0),
|
padding: const EdgeInsets.all(6.0),
|
||||||
child: const Text(
|
child: Text(
|
||||||
"backup_controller_page_created",
|
"backup_controller_page_created",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 10.0,
|
fontSize: 10.0,
|
||||||
),
|
),
|
||||||
|
@ -131,16 +128,14 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
decoration: const BoxDecoration(
|
|
||||||
// color: Colors.grey[100],
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
TableCell(
|
TableCell(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(6.0),
|
padding: const EdgeInsets.all(6.0),
|
||||||
child: const Text(
|
child: Text(
|
||||||
"backup_controller_page_id",
|
"backup_controller_page_id",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 10.0,
|
fontSize: 10.0,
|
||||||
),
|
),
|
||||||
|
@ -181,8 +176,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 10.0,
|
minHeight: 10.0,
|
||||||
value: uploadProgress / 100.0,
|
value: uploadProgress / 100.0,
|
||||||
backgroundColor: Colors.grey,
|
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
|
@ -214,8 +208,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 10.0,
|
minHeight: 10.0,
|
||||||
value: uploadProgress / 100.0,
|
value: uploadProgress / 100.0,
|
||||||
backgroundColor: Colors.grey,
|
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -57,6 +57,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
||||||
? 'assets/immich-text-dark.png'
|
? 'assets/immich-text-dark.png'
|
||||||
: 'assets/immich-text-light.png',
|
: 'assets/immich-text-light.png',
|
||||||
height: 16,
|
height: 16,
|
||||||
|
color: context.primaryColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -88,7 +89,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
||||||
|
|
||||||
buildSettingButton() {
|
buildSettingButton() {
|
||||||
return buildActionButton(
|
return buildActionButton(
|
||||||
Icons.settings_rounded,
|
Icons.settings_outlined,
|
||||||
"profile_drawer_settings",
|
"profile_drawer_settings",
|
||||||
() => context.pushRoute(const SettingsRoute()),
|
() => context.pushRoute(const SettingsRoute()),
|
||||||
);
|
);
|
||||||
|
@ -146,9 +147,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.surface,
|
||||||
? context.scaffoldBackgroundColor
|
|
||||||
: const Color.fromARGB(255, 225, 229, 240),
|
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
minLeadingWidth: 50,
|
minLeadingWidth: 50,
|
||||||
|
@ -171,10 +170,10 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 5.0,
|
minHeight: 10.0,
|
||||||
value: percentage,
|
value: percentage,
|
||||||
backgroundColor: Colors.grey,
|
borderRadius:
|
||||||
color: theme.primaryColor,
|
const BorderRadius.all(Radius.circular(10.0)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -248,7 +247,6 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
||||||
right: horizontalPadding,
|
right: horizontalPadding,
|
||||||
bottom: isHorizontal ? 20 : 100,
|
bottom: isHorizontal ? 20 : 100,
|
||||||
),
|
),
|
||||||
backgroundColor: theme.cardColor,
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.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/providers/upload_profile_image.provider.dart';
|
import 'package:immich_mobile/providers/upload_profile_image.provider.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
@ -79,9 +80,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.surface,
|
||||||
? context.scaffoldBackgroundColor
|
|
||||||
: const Color.fromARGB(255, 225, 229, 240),
|
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(10),
|
topLeft: Radius.circular(10),
|
||||||
topRight: Radius.circular(10),
|
topRight: Radius.circular(10),
|
||||||
|
@ -99,9 +98,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
||||||
bottom: -5,
|
bottom: -5,
|
||||||
right: -8,
|
right: -8,
|
||||||
child: Material(
|
child: Material(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.surfaceContainerHighest,
|
||||||
? Colors.blueGrey[800]
|
|
||||||
: Colors.white,
|
|
||||||
elevation: 3,
|
elevation: 3,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(50.0),
|
borderRadius: BorderRadius.circular(50.0),
|
||||||
|
@ -129,7 +126,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
authState.userEmail,
|
authState.userEmail,
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: context.textTheme.bodySmall?.color?.withAlpha(200),
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||||
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/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
|
@ -42,9 +43,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.isDarkTheme
|
color: context.colorScheme.surface,
|
||||||
? context.scaffoldBackgroundColor
|
|
||||||
: const Color.fromARGB(255, 225, 229, 240),
|
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
bottomLeft: Radius.circular(10),
|
bottomLeft: Radius.circular(10),
|
||||||
bottomRight: Radius.circular(10),
|
bottomRight: Radius.circular(10),
|
||||||
|
@ -71,10 +70,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Divider(
|
child: Divider(thickness: 1),
|
||||||
color: Color.fromARGB(101, 201, 201, 201),
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -100,8 +96,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
"${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}",
|
"${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: contentFontSize,
|
fontSize: contentFontSize,
|
||||||
color: context.textTheme.labelSmall?.color
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
?.withOpacity(0.5),
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -111,10 +106,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Divider(
|
child: Divider(thickness: 1),
|
||||||
color: Color.fromARGB(101, 201, 201, 201),
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -142,8 +134,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
: "--",
|
: "--",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: contentFontSize,
|
fontSize: contentFontSize,
|
||||||
color: context.textTheme.labelSmall?.color
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
?.withOpacity(0.5),
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -153,10 +144,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Divider(
|
child: Divider(thickness: 1),
|
||||||
color: Color.fromARGB(101, 201, 201, 201),
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -197,8 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
getServerUrl() ?? '--',
|
getServerUrl() ?? '--',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: contentFontSize,
|
fontSize: contentFontSize,
|
||||||
color: context.textTheme.labelSmall?.color
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
?.withOpacity(0.5),
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
@ -211,10 +198,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Divider(
|
child: Divider(thickness: 1),
|
||||||
color: Color.fromARGB(101, 201, 201, 201),
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -255,8 +239,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
: "--",
|
: "--",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: contentFontSize,
|
fontSize: contentFontSize,
|
||||||
color: context.textTheme.labelSmall?.color
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
?.withOpacity(0.5),
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -47,7 +47,7 @@ class ConfirmDialog extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
ok,
|
ok,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red[400],
|
color: context.colorScheme.error,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
|
|
|
@ -111,7 +111,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
|
|
||||||
buildBackupIndicator() {
|
buildBackupIndicator() {
|
||||||
final indicatorIcon = getBackupBadgeIcon();
|
final indicatorIcon = getBackupBadgeIcon();
|
||||||
final badgeBackground = isDarkTheme ? Colors.blueGrey[800] : Colors.white;
|
final badgeBackground = context.colorScheme.surfaceContainer;
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => context.pushRoute(const BackupControllerRoute()),
|
onTap: () => context.pushRoute(const BackupControllerRoute()),
|
||||||
|
@ -123,7 +123,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: badgeBackground,
|
color: badgeBackground,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isDarkTheme ? Colors.black : Colors.grey,
|
color: context.colorScheme.outline.withOpacity(.3),
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||||
),
|
),
|
||||||
|
|
|
@ -21,6 +21,7 @@ class ImmichTitleText extends StatelessWidget {
|
||||||
),
|
),
|
||||||
width: fontSize * 4,
|
width: fontSize * 4,
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
|
color: context.primaryColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,9 @@ class ImmichToast {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(5.0),
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
color: context.colorScheme.surfaceContainer,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.black12,
|
color: context.colorScheme.outline.withOpacity(.5),
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ChangePasswordForm extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.grey[700],
|
color: context.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -191,9 +191,6 @@ class ChangePasswordButton extends ConsumerWidget {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
visualDensity: VisualDensity.standard,
|
visualDensity: VisualDensity.standard,
|
||||||
backgroundColor: context.primaryColor,
|
|
||||||
foregroundColor: Colors.grey[50],
|
|
||||||
elevation: 2,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
|
||||||
),
|
),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
|
|
@ -70,6 +70,7 @@ class _MapThemeOverideState extends ConsumerState<MapThemeOveride>
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_theme = widget.themeMode ??
|
_theme = widget.themeMode ??
|
||||||
ref.watch(mapStateNotifierProvider.select((v) => v.themeMode));
|
ref.watch(mapStateNotifierProvider.select((v) => v.themeMode));
|
||||||
|
var appTheme = ref.watch(immichThemeProvider);
|
||||||
|
|
||||||
useValueChanged<ThemeMode, void>(_theme, (_, __) {
|
useValueChanged<ThemeMode, void>(_theme, (_, __) {
|
||||||
if (_theme == ThemeMode.system) {
|
if (_theme == ThemeMode.system) {
|
||||||
|
@ -83,7 +84,9 @@ class _MapThemeOverideState extends ConsumerState<MapThemeOveride>
|
||||||
});
|
});
|
||||||
|
|
||||||
return Theme(
|
return Theme(
|
||||||
data: _isDarkTheme ? immichDarkTheme : immichLightTheme,
|
data: _isDarkTheme
|
||||||
|
? getThemeData(colorScheme: appTheme.dark)
|
||||||
|
: getThemeData(colorScheme: appTheme.light),
|
||||||
child: widget.mapBuilder.call(
|
child: widget.mapBuilder.call(
|
||||||
ref.watch(
|
ref.watch(
|
||||||
mapStateNotifierProvider.select(
|
mapStateNotifierProvider.select(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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:immich_mobile/constants/immich_colors.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class MemoryEpilogue extends StatefulWidget {
|
class MemoryEpilogue extends StatefulWidget {
|
||||||
|
@ -49,24 +48,26 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Icons.check_circle_outline_sharp,
|
Icons.check_circle_outline_sharp,
|
||||||
color: immichDarkThemePrimaryColor,
|
color: context.isDarkTheme
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: context.colorScheme.inversePrimary,
|
||||||
size: 64.0,
|
size: 64.0,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
Text(
|
Text(
|
||||||
"memories_all_caught_up",
|
"memories_all_caught_up",
|
||||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
style: context.textTheme.headlineMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
Text(
|
Text(
|
||||||
"memories_check_back_tomorrow",
|
"memories_check_back_tomorrow",
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -74,7 +75,9 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
||||||
child: Text(
|
child: Text(
|
||||||
"memories_start_over",
|
"memories_start_over",
|
||||||
style: context.textTheme.displayMedium?.copyWith(
|
style: context.textTheme.displayMedium?.copyWith(
|
||||||
color: immichDarkThemePrimaryColor,
|
color: context.isDarkTheme
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: context.colorScheme.inversePrimary,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
),
|
),
|
||||||
|
@ -108,9 +111,9 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"memories_swipe_to_close",
|
"memories_swipe_to_close",
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class MemoryProgressIndicator extends StatelessWidget {
|
class MemoryProgressIndicator extends StatelessWidget {
|
||||||
/// The number of ticks in the progress indicator
|
/// The number of ticks in the progress indicator
|
||||||
|
@ -25,8 +25,11 @@ class MemoryProgressIndicator extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
value: value,
|
value: value,
|
||||||
backgroundColor: Colors.grey[600],
|
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
|
||||||
color: immichDarkThemePrimaryColor,
|
backgroundColor: Colors.grey[800],
|
||||||
|
color: context.isDarkTheme
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: context.colorScheme.inversePrimary,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
|
|
@ -22,9 +22,9 @@ class SearchFilterChip extends StatelessWidget {
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
color: context.primaryColor.withAlpha(25),
|
color: context.primaryColor.withOpacity(.5),
|
||||||
shape: StadiumBorder(
|
shape: StadiumBorder(
|
||||||
side: BorderSide(color: context.primaryColor),
|
side: BorderSide(color: context.colorScheme.secondaryContainer),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding:
|
||||||
|
@ -47,8 +47,9 @@ class SearchFilterChip extends StatelessWidget {
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
shape:
|
shape: StadiumBorder(
|
||||||
StadiumBorder(side: BorderSide(color: Colors.grey.withAlpha(100))),
|
side: BorderSide(color: context.colorScheme.outline.withOpacity(.5)),
|
||||||
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0),
|
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
class ThumbnailWithInfoContainer extends StatelessWidget {
|
class ThumbnailWithInfoContainer extends StatelessWidget {
|
||||||
const ThumbnailWithInfoContainer({
|
const ThumbnailWithInfoContainer({
|
||||||
|
@ -25,7 +26,14 @@ class ThumbnailWithInfoContainer extends StatelessWidget {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
context.colorScheme.surfaceContainer,
|
||||||
|
context.colorScheme.surfaceContainer.darken(amount: .1),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
foregroundDecoration: BoxDecoration(
|
foregroundDecoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
|
@ -34,7 +42,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget {
|
||||||
begin: FractionalOffset.topCenter,
|
begin: FractionalOffset.topCenter,
|
||||||
end: FractionalOffset.bottomCenter,
|
end: FractionalOffset.bottomCenter,
|
||||||
colors: [
|
colors: [
|
||||||
Colors.grey.withOpacity(0.0),
|
Colors.transparent,
|
||||||
label == ''
|
label == ''
|
||||||
? Colors.black.withOpacity(0.1)
|
? Colors.black.withOpacity(0.1)
|
||||||
: Colors.black.withOpacity(0.5),
|
: Colors.black.withOpacity(0.5),
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||||
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: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/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
|
||||||
class CustomeProxyHeaderSettings extends StatelessWidget {
|
class CustomeProxyHeaderSettings extends StatelessWidget {
|
||||||
|
@ -20,8 +21,8 @@ class CustomeProxyHeaderSettings extends StatelessWidget {
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"headers_settings_tile_subtitle".tr(),
|
"headers_settings_tile_subtitle".tr(),
|
||||||
style: const TextStyle(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontSize: 14,
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () => context.pushRoute(const HeaderSettingsRoute()),
|
onTap: () => context.pushRoute(const HeaderSettingsRoute()),
|
||||||
|
|
|
@ -40,9 +40,7 @@ class LanguageSettings extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
backgroundColor: WidgetStatePropertyAll<Color>(
|
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||||
context.isDarkTheme
|
context.colorScheme.surfaceContainer,
|
||||||
? Colors.grey[900]!
|
|
||||||
: context.scaffoldBackgroundColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
menuHeight: context.height * 0.5,
|
menuHeight: context.height * 0.5,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart' show useEffect, useState;
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
|
|
||||||
class LocalStorageSettings extends HookConsumerWidget {
|
class LocalStorageSettings extends HookConsumerWidget {
|
||||||
|
@ -35,10 +36,10 @@ class LocalStorageSettings extends HookConsumerWidget {
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
).tr(args: ["${cacheItemCount.value}"]),
|
).tr(args: ["${cacheItemCount.value}"]),
|
||||||
subtitle: const Text(
|
subtitle: Text(
|
||||||
"cache_settings_duplicated_assets_subtitle",
|
"cache_settings_duplicated_assets_subtitle",
|
||||||
style: TextStyle(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontSize: 14,
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
).tr(),
|
).tr(),
|
||||||
trailing: TextButton(
|
trailing: TextButton(
|
||||||
|
|
|
@ -15,6 +15,9 @@ class PreferenceSetting extends StatelessWidget {
|
||||||
HapticSetting(),
|
HapticSetting(),
|
||||||
];
|
];
|
||||||
|
|
||||||
return const SettingsSubPageScaffold(settings: preferenceSettings);
|
return const SettingsSubPageScaffold(
|
||||||
|
settings: preferenceSettings,
|
||||||
|
showDivider: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
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/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.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';
|
||||||
|
|
||||||
|
class PrimaryColorSetting extends HookConsumerWidget {
|
||||||
|
const PrimaryColorSetting({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final themeProvider = ref.read(immichThemeProvider);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUseSystemColorChange(bool newValue) {
|
||||||
|
systemPrimaryColorSetting.value = newValue;
|
||||||
|
ref.watch(dynamicThemeSettingProvider.notifier).state = newValue;
|
||||||
|
ref.invalidate(immichThemeProvider);
|
||||||
|
popBottomSheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPrimaryColorChange(ImmichColorPreset colorPreset) {
|
||||||
|
primaryColorSetting.value = colorPreset.name;
|
||||||
|
ref.watch(immichThemePresetProvider.notifier).state = colorPreset;
|
||||||
|
ref.invalidate(immichThemeProvider);
|
||||||
|
|
||||||
|
//turn off system color setting
|
||||||
|
if (systemPrimaryColorSetting.value) {
|
||||||
|
onUseSystemColorChange(false);
|
||||||
|
} else {
|
||||||
|
popBottomSheet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPrimaryColorTile({
|
||||||
|
required Color topColor,
|
||||||
|
required Color bottomColor,
|
||||||
|
required double tileSize,
|
||||||
|
required bool showSelector,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.all(4.0),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: tileSize,
|
||||||
|
width: tileSize,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bottomColor,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(100)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: tileSize / 2,
|
||||||
|
width: tileSize,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: topColor,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(100),
|
||||||
|
topRight: Radius.circular(100),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (showSelector)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(100)),
|
||||||
|
color: Colors.grey[900]?.withOpacity(.4),
|
||||||
|
),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(3),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetContent() {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
"theme_setting_primary_color_title".tr(),
|
||||||
|
style: context.textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isDynamicThemeAvailable)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
margin: const EdgeInsets.only(top: 10),
|
||||||
|
child: SwitchListTile.adaptive(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 6, horizontal: 20),
|
||||||
|
dense: true,
|
||||||
|
activeColor: context.primaryColor,
|
||||||
|
tileColor: context.colorScheme.surfaceContainerHigh,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'theme_setting_system_primary_color_title'.tr(),
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
value: systemPrimaryColorSetting.value,
|
||||||
|
onChanged: onUseSystemColorChange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: ImmichColorPreset.values.map((themePreset) {
|
||||||
|
var theme = themePreset.getTheme();
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => onPrimaryColorChange(themePreset),
|
||||||
|
child: buildPrimaryColorTile(
|
||||||
|
topColor: theme.light.primary,
|
||||||
|
bottomColor: theme.dark.primary,
|
||||||
|
tileSize: tileSize,
|
||||||
|
showSelector: currentPreset.value == themePreset &&
|
||||||
|
!systemPrimaryColorSetting.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
onTap: () => showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0),
|
||||||
|
child: bottomSheetContent(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"theme_setting_primary_color_title".tr(),
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"theme_setting_primary_color_subtitle".tr(),
|
||||||
|
style: context.textTheme.bodyMedium
|
||||||
|
?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 8.0),
|
||||||
|
child: buildPrimaryColorTile(
|
||||||
|
topColor: themeProvider.light.primary,
|
||||||
|
bottomColor: themeProvider.dark.primary,
|
||||||
|
tileSize: 42.0,
|
||||||
|
showSelector: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ 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/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/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';
|
||||||
|
@ -16,11 +17,16 @@ class ThemeSetting extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final currentThemeString = useAppSettingsState(AppSettingsEnum.themeMode);
|
final currentThemeString = useAppSettingsState(AppSettingsEnum.themeMode);
|
||||||
final currentTheme = useValueNotifier(ref.read(immichThemeProvider));
|
final currentTheme = useValueNotifier(ref.read(immichThemeModeProvider));
|
||||||
final isDarkTheme = useValueNotifier(currentTheme.value == ThemeMode.dark);
|
final isDarkTheme = useValueNotifier(currentTheme.value == ThemeMode.dark);
|
||||||
final isSystemTheme =
|
final isSystemTheme =
|
||||||
useValueNotifier(currentTheme.value == ThemeMode.system);
|
useValueNotifier(currentTheme.value == ThemeMode.system);
|
||||||
|
|
||||||
|
final applyThemeToBackgroundSetting =
|
||||||
|
useAppSettingsState(AppSettingsEnum.colorfulInterface);
|
||||||
|
final applyThemeToBackgroundProvider =
|
||||||
|
useValueNotifier(ref.read(colorfulInterfaceSettingProvider));
|
||||||
|
|
||||||
useValueChanged(
|
useValueChanged(
|
||||||
currentThemeString.value,
|
currentThemeString.value,
|
||||||
(_, __) => currentTheme.value = switch (currentThemeString.value) {
|
(_, __) => currentTheme.value = switch (currentThemeString.value) {
|
||||||
|
@ -30,12 +36,18 @@ class ThemeSetting extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useValueChanged(
|
||||||
|
applyThemeToBackgroundSetting.value,
|
||||||
|
(_, __) => applyThemeToBackgroundProvider.value =
|
||||||
|
applyThemeToBackgroundSetting.value,
|
||||||
|
);
|
||||||
|
|
||||||
void onThemeChange(bool isDark) {
|
void onThemeChange(bool isDark) {
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark;
|
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.dark;
|
||||||
currentThemeString.value = "dark";
|
currentThemeString.value = "dark";
|
||||||
} else {
|
} else {
|
||||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.light;
|
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.light;
|
||||||
currentThemeString.value = "light";
|
currentThemeString.value = "light";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +56,7 @@ class ThemeSetting extends HookConsumerWidget {
|
||||||
if (isSystem) {
|
if (isSystem) {
|
||||||
currentThemeString.value = "system";
|
currentThemeString.value = "system";
|
||||||
isSystemTheme.value = true;
|
isSystemTheme.value = true;
|
||||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.system;
|
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.system;
|
||||||
} else {
|
} else {
|
||||||
final currentSystemBrightness =
|
final currentSystemBrightness =
|
||||||
MediaQuery.platformBrightnessOf(context);
|
MediaQuery.platformBrightnessOf(context);
|
||||||
|
@ -52,14 +64,20 @@ class ThemeSetting extends HookConsumerWidget {
|
||||||
isDarkTheme.value = currentSystemBrightness == Brightness.dark;
|
isDarkTheme.value = currentSystemBrightness == Brightness.dark;
|
||||||
if (currentSystemBrightness == Brightness.light) {
|
if (currentSystemBrightness == Brightness.light) {
|
||||||
currentThemeString.value = "light";
|
currentThemeString.value = "light";
|
||||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.light;
|
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.light;
|
||||||
} else if (currentSystemBrightness == Brightness.dark) {
|
} else if (currentSystemBrightness == Brightness.dark) {
|
||||||
currentThemeString.value = "dark";
|
currentThemeString.value = "dark";
|
||||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark;
|
ref.watch(immichThemeModeProvider.notifier).state = ThemeMode.dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onSurfaceColorSettingChange(bool useColorfulInterface) {
|
||||||
|
applyThemeToBackgroundSetting.value = useColorfulInterface;
|
||||||
|
ref.watch(colorfulInterfaceSettingProvider.notifier).state =
|
||||||
|
useColorfulInterface;
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -75,6 +93,13 @@ class ThemeSetting extends HookConsumerWidget {
|
||||||
title: 'theme_setting_dark_mode_switch'.tr(),
|
title: 'theme_setting_dark_mode_switch'.tr(),
|
||||||
onChanged: onThemeChange,
|
onChanged: onThemeChange,
|
||||||
),
|
),
|
||||||
|
const PrimaryColorSetting(),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
valueNotifier: applyThemeToBackgroundProvider,
|
||||||
|
title: "theme_setting_colorful_interface_title".tr(),
|
||||||
|
subtitle: 'theme_setting_colorful_interface_subtitle'.tr(),
|
||||||
|
onChanged: onSurfaceColorSettingChange,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
class SettingsButtonListTile extends StatelessWidget {
|
class SettingsButtonListTile extends StatelessWidget {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
@ -39,7 +40,12 @@ class SettingsButtonListTile extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
if (subtileText != null) const SizedBox(height: 4),
|
if (subtileText != null) const SizedBox(height: 4),
|
||||||
if (subtileText != null)
|
if (subtileText != null)
|
||||||
Text(subtileText!, style: context.textTheme.bodyMedium),
|
Text(
|
||||||
|
subtileText!,
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (subtitle != null) subtitle!,
|
if (subtitle != null) subtitle!,
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)),
|
ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
class SettingsSwitchListTile extends StatelessWidget {
|
class SettingsSwitchListTile extends StatelessWidget {
|
||||||
final ValueNotifier<bool> valueNotifier;
|
final ValueNotifier<bool> valueNotifier;
|
||||||
|
@ -54,7 +55,9 @@ class SettingsSwitchListTile extends StatelessWidget {
|
||||||
? Text(
|
? Text(
|
||||||
subtitle!,
|
subtitle!,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: enabled ? null : context.themeData.disabledColor,
|
color: enabled
|
||||||
|
? context.colorScheme.onSurfaceSecondary
|
||||||
|
: context.themeData.disabledColor,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.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/utils/http_ssl_cert_override.dart';
|
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
||||||
|
|
||||||
class SslClientCertSettings extends StatefulWidget {
|
class SslClientCertSettings extends StatefulWidget {
|
||||||
|
@ -40,7 +41,9 @@ class _SslClientCertSettingsState extends State<SslClientCertSettings> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"client_cert_subtitle".tr(),
|
"client_cert_subtitle".tr(),
|
||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 6,
|
height: 6,
|
||||||
|
|
|
@ -65,8 +65,8 @@ class SharedLinkItem extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final themeData = context.themeData;
|
final colorScheme = context.colorScheme;
|
||||||
final isDarkMode = themeData.brightness == Brightness.dark;
|
final isDarkMode = colorScheme.brightness == Brightness.dark;
|
||||||
final thumbnailUrl = sharedLink.thumbAssetId != null
|
final thumbnailUrl = sharedLink.thumbAssetId != null
|
||||||
? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!)
|
? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!)
|
||||||
: null;
|
: null;
|
||||||
|
@ -159,7 +159,7 @@ class SharedLinkItem extends ConsumerWidget {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
padding: const EdgeInsets.only(right: 10),
|
||||||
child: Chip(
|
child: Chip(
|
||||||
backgroundColor: themeData.primaryColor,
|
backgroundColor: colorScheme.primary,
|
||||||
label: Text(
|
label: Text(
|
||||||
labelText,
|
labelText,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -240,7 +240,7 @@ class SharedLinkItem extends ConsumerWidget {
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
verticalOffset: 0,
|
verticalOffset: 0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: themeData.primaryColor.withOpacity(0.9),
|
color: colorScheme.primary.withOpacity(0.9),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
|
@ -253,7 +253,7 @@ class SharedLinkItem extends ConsumerWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
sharedLink.title,
|
sharedLink.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: themeData.primaryColor,
|
color: colorScheme.primary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
@ -268,7 +268,7 @@ class SharedLinkItem extends ConsumerWidget {
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
verticalOffset: 0,
|
verticalOffset: 0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: themeData.primaryColor.withOpacity(0.9),
|
color: colorScheme.primary.withOpacity(0.9),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
|
|
|
@ -377,6 +377,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
|
dynamic_color:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dynamic_color
|
||||||
|
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.0"
|
||||||
easy_image_viewer:
|
easy_image_viewer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -61,9 +61,11 @@ dependencies:
|
||||||
octo_image: ^2.0.0
|
octo_image: ^2.0.0
|
||||||
thumbhash: 0.1.0+1
|
thumbhash: 0.1.0+1
|
||||||
async: ^2.11.0
|
async: ^2.11.0
|
||||||
|
dynamic_color: ^1.7.0 #package to apply system theme
|
||||||
|
|
||||||
#image editing packages
|
#image editing packages
|
||||||
crop_image: ^1.0.13
|
crop_image: ^1.0.13
|
||||||
|
|
||||||
openapi:
|
openapi:
|
||||||
path: openapi
|
path: openapi
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue