Kaynağa Gözat

wip(mobile): unify-colors

shalong-tanwen 1 yıl önce
ebeveyn
işleme
ecb1669ab8
37 değiştirilmiş dosya ile 324 ekleme ve 243 silme
  1. 9 1
      mobile/lib/extensions/build_context_extensions.dart
  2. 27 0
      mobile/lib/extensions/color_extensions.dart
  3. 21 0
      mobile/lib/extensions/widgetref_extensions.dart
  4. 4 1
      mobile/lib/main.dart
  5. 17 14
      mobile/lib/modules/activities/views/activities_page.dart
  6. 2 6
      mobile/lib/modules/album/ui/album_thumbnail_card.dart
  7. 2 10
      mobile/lib/modules/album/ui/album_title_text_field.dart
  8. 11 10
      mobile/lib/modules/album/ui/album_viewer_appbar.dart
  9. 1 6
      mobile/lib/modules/album/ui/album_viewer_editable_title.dart
  10. 9 10
      mobile/lib/modules/album/views/album_options_part.dart
  11. 6 5
      mobile/lib/modules/album/views/album_viewer_page.dart
  12. 3 4
      mobile/lib/modules/album/views/library_page.dart
  13. 8 10
      mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart
  14. 8 10
      mobile/lib/modules/album/views/select_user_for_sharing_page.dart
  15. 3 4
      mobile/lib/modules/album/views/sharing_page.dart
  16. 32 38
      mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart
  17. 2 3
      mobile/lib/modules/asset_viewer/ui/description_input.dart
  18. 1 1
      mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart
  19. 1 1
      mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart
  20. 3 1
      mobile/lib/modules/home/ui/control_bottom_app_bar.dart
  21. 2 2
      mobile/lib/modules/home/views/home_page.dart
  22. 11 6
      mobile/lib/modules/map/views/map_page.dart
  23. 8 4
      mobile/lib/modules/settings/ui/settings_switch_list_tile.dart
  24. 2 4
      mobile/lib/modules/trash/views/trash_page.dart
  25. 0 8
      mobile/lib/routing/tab_navigation_observer.dart
  26. 14 0
      mobile/lib/shared/providers/immich_loading_overlay.provider.dart
  27. 1 1
      mobile/lib/shared/ui/confirm_dialog.dart
  28. 3 6
      mobile/lib/shared/ui/immich_app_bar.dart
  29. 4 4
      mobile/lib/shared/ui/immich_loading_indicator.dart
  30. 21 23
      mobile/lib/shared/ui/immich_toast.dart
  31. 1 1
      mobile/lib/shared/ui/user_circle_avatar.dart
  32. 3 5
      mobile/lib/shared/views/app_log_detail_page.dart
  33. 11 15
      mobile/lib/shared/views/app_log_page.dart
  34. 61 29
      mobile/lib/shared/views/immich_loading_overlay.dart
  35. 3 0
      mobile/lib/utils/immich_app_theme.dart
  36. 8 0
      mobile/pubspec.lock
  37. 1 0
      mobile/pubspec.yaml

+ 9 - 1
mobile/lib/extensions/build_context_extensions.dart

@@ -1,5 +1,7 @@
 import 'package:auto_route/auto_route.dart';
+import 'package:dynamic_color/dynamic_color.dart';
 import 'package:flutter/material.dart';
+import 'package:immich_mobile/utils/immich_app_theme.dart';
 
 extension ContextHelper on BuildContext {
   // Returns the current size from MediaQuery
@@ -32,6 +34,12 @@ extension ContextHelper on BuildContext {
   // Current ColorScheme used
   ColorScheme get colorScheme => themeData.colorScheme;
 
+  // Red Accent harmonized with primary color
+  Color get redColor => redAccent.harmonizeWith(primaryColor);
+
+  // Orange Accent harmonized with primary color
+  Color get orangeColor => orangeAccent.harmonizeWith(primaryColor);
+
   // Pop-out from the current context with optional result
   void pop<T>([T? result]) => Navigator.of(this).pop(result);
 
@@ -45,7 +53,7 @@ extension ContextHelper on BuildContext {
   ) =>
       AutoRouter.of(this).navigate(route);
 
-// Auto-Push replace route from the current context
+  // Auto-Push replace route from the current context
   Future<T?> autoReplace<T extends Object?>(PageRouteInfo<dynamic> route) =>
       AutoRouter.of(this).replace(route);
 

+ 27 - 0
mobile/lib/extensions/color_extensions.dart

@@ -0,0 +1,27 @@
+import 'dart:ui';
+
+extension LightenDarken on Color {
+  /// Darken a color by [percent] amount (100 = black)
+  Color darken([int percent = 10]) {
+    assert(1 <= percent && percent <= 100);
+    var f = 1 - percent / 100;
+    return Color.fromARGB(
+      alpha,
+      (red * f).round(),
+      (green * f).round(),
+      (blue * f).round(),
+    );
+  }
+
+  /// Lighten a color by [percent] amount (100 = white)
+  Color lighten([int percent = 10]) {
+    assert(1 <= percent && percent <= 100);
+    var p = percent / 100;
+    return Color.fromARGB(
+      alpha,
+      red + ((255 - red) * p).round(),
+      green + ((255 - green) * p).round(),
+      blue + ((255 - blue) * p).round(),
+    );
+  }
+}

+ 21 - 0
mobile/lib/extensions/widgetref_extensions.dart

@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/shared/providers/immich_loading_overlay.provider.dart';
+
+extension LoadingOverlay on WidgetRef {
+  ValueNotifier<bool> useProcessingOverlay() {
+    final result = useState(false);
+    final immichOverlayController =
+        read(immichLoadingOverlayController.notifier);
+    useValueChanged(
+      result.value,
+      (_, __) => result.value
+          ? WidgetsBinding.instance
+              .addPostFrameCallback((_) => immichOverlayController.show())
+          : WidgetsBinding.instance
+              .addPostFrameCallback((_) => immichOverlayController.hide()),
+    );
+    return result;
+  }
+}

+ 4 - 1
mobile/lib/main.dart

@@ -216,7 +216,10 @@ class ImmichAppState extends ConsumerState<ImmichApp>
               navigatorObservers: () => [TabNavigationObserver(ref: ref)],
             ),
           ),
-          const ImmichLoadingOverlay(),
+          ImmichLoadingOverlay(
+            darkTheme: immichDarkTheme,
+            theme: immichLightTheme,
+          ),
           const VersionAnnouncementOverlay(),
         ],
       ),

+ 17 - 14
mobile/lib/modules/activities/views/activities_page.dart

@@ -50,9 +50,8 @@ class ActivitiesPage extends HookConsumerWidget {
     );
 
     buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) {
-      final textColor = context.isDarkTheme ? Colors.white : Colors.black;
-      final textStyle = context.textTheme.bodyMedium
-          ?.copyWith(color: textColor.withOpacity(0.6));
+      final textStyle = context.textTheme.bodyMedium?.copyWith(
+          color: context.textTheme.bodyMedium?.color?.withOpacity(0.6));
 
       return Row(
         mainAxisAlignment: leftAlign
@@ -150,14 +149,14 @@ class ActivitiesPage extends HookConsumerWidget {
                 },
               ),
             ),
-            suffixIconColor: liked ? Colors.red[700] : null,
+            suffixIconColor: liked ? context.redColor : null,
             hintText: isReadOnly
                 ? 'shared_album_activities_input_disable'.tr()
                 : 'shared_album_activities_input_hint'.tr(),
             hintStyle: TextStyle(
               fontWeight: FontWeight.normal,
               fontSize: 14,
-              color: Colors.grey[600],
+              color: context.themeData.hintColor,
             ),
           ),
           onEditingComplete: () async {
@@ -200,27 +199,31 @@ class ActivitiesPage extends HookConsumerWidget {
         onDismissed: (direction) async =>
             await ref.read(provider.notifier).removeActivity(activity.id),
         background: Container(
-          color: canDelete ? Colors.red[400] : Colors.grey[600],
+          color: canDelete
+              ? context.colorScheme.error
+              : context.themeData.disabledColor,
           alignment: AlignmentDirectional.centerStart,
           child: canDelete
-              ? const Padding(
-                  padding: EdgeInsets.all(15),
+              ? Padding(
+                  padding: const EdgeInsets.all(15),
                   child: Icon(
                     Icons.delete_sweep_rounded,
-                    color: Colors.black,
+                    color: context.colorScheme.surface,
                   ),
                 )
               : null,
         ),
         secondaryBackground: Container(
-          color: canDelete ? Colors.red[400] : Colors.grey[600],
+          color: canDelete
+              ? context.colorScheme.error
+              : context.themeData.disabledColor,
           alignment: AlignmentDirectional.centerEnd,
           child: canDelete
-              ? const Padding(
-                  padding: EdgeInsets.all(15),
+              ? Padding(
+                  padding: const EdgeInsets.all(15),
                   child: Icon(
                     Icons.delete_sweep_rounded,
-                    color: Colors.black,
+                    color: context.colorScheme.surface,
                   ),
                 )
               : null,
@@ -288,7 +291,7 @@ class ActivitiesPage extends HookConsumerWidget {
                                   alignment: Alignment.center,
                                   child: Icon(
                                     Icons.favorite_rounded,
-                                    color: Colors.red[700],
+                                    color: context.redColor,
                                   ),
                                 ),
                                 title: buildTitleWithTimestamp(activity),

+ 2 - 6
mobile/lib/modules/album/ui/album_thumbnail_card.dart

@@ -23,8 +23,6 @@ class AlbumThumbnailCard extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    var isDarkTheme = context.isDarkTheme;
-
     return LayoutBuilder(
       builder: (context, constraints) {
         var cardSize = constraints.maxWidth;
@@ -83,7 +81,7 @@ class AlbumThumbnailCard extends StatelessWidget {
                   style: TextStyle(
                     fontFamily: 'WorkSans',
                     fontSize: 12,
-                    color: isDarkTheme ? Colors.white : Colors.black,
+                    color: context.colorScheme.onSurface,
                   ),
                 ),
                 if (owner != null) const TextSpan(text: ' · '),
@@ -124,9 +122,7 @@ class AlbumThumbnailCard extends StatelessWidget {
                           album.name,
                           style: TextStyle(
                             fontWeight: FontWeight.bold,
-                            color: isDarkTheme
-                                ? context.primaryColor
-                                : Colors.black,
+                            color: context.primaryColor,
                           ),
                         ),
                       ),

+ 2 - 10
mobile/lib/modules/album/ui/album_title_text_field.dart

@@ -20,8 +20,6 @@ class AlbumTitleTextField extends ConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final isDarkTheme = context.isDarkTheme;
-
     return TextField(
       onChanged: (v) {
         if (v.isEmpty) {
@@ -33,9 +31,8 @@ class AlbumTitleTextField extends ConsumerWidget {
         ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
       },
       focusNode: albumTitleTextFieldFocusNode,
-      style: TextStyle(
+      style: const TextStyle(
         fontSize: 28,
-        color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
         fontWeight: FontWeight.bold,
       ),
       controller: albumTitleController,
@@ -70,15 +67,10 @@ class AlbumTitleTextField extends ConsumerWidget {
           borderRadius: BorderRadius.circular(10),
         ),
         hintText: 'share_add_title'.tr(),
-        hintStyle: TextStyle(
+        hintStyle: const TextStyle(
           fontSize: 28,
-          color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
           fontWeight: FontWeight.bold,
         ),
-        focusColor: Colors.grey[300],
-        fillColor: isDarkTheme
-            ? const Color.fromARGB(255, 32, 33, 35)
-            : Colors.grey[200],
         filled: isAlbumTitleTextFieldFocus.value,
       ),
     );

+ 11 - 10
mobile/lib/modules/album/ui/album_viewer_appbar.dart

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/widgetref_extensions.dart';
 import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
 import 'package:immich_mobile/modules/album/providers/album.provider.dart';
 import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
@@ -14,7 +15,6 @@ import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/models/album.dart';
 import 'package:immich_mobile/shared/models/asset.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
-import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
 
 class AlbumViewerAppbar extends HookConsumerWidget
     implements PreferredSizeWidget {
@@ -43,6 +43,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
   Widget build(BuildContext context, WidgetRef ref) {
     final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
     final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
+    final immichOverlayController = ref.useProcessingOverlay();
     final comments = album.shared
         ? ref.watch(
             activityStatisticsStateProvider(
@@ -52,7 +53,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
         : 0;
 
     deleteAlbum() async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
 
       final bool success;
       if (album.shared) {
@@ -74,7 +75,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
         );
       }
 
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     Future<void> showConfirmationDialog() async {
@@ -107,7 +108,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
                   'Confirm',
                   style: TextStyle(
                     fontWeight: FontWeight.bold,
-                    color: !context.isDarkTheme ? Colors.red : Colors.red[300],
+                    color: context.colorScheme.error,
                   ),
                 ),
               ),
@@ -122,7 +123,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
     }
 
     void onLeaveAlbumPressed() async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
 
       bool isSuccess =
           await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
@@ -140,11 +141,11 @@ class AlbumViewerAppbar extends HookConsumerWidget
         );
       }
 
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     void onRemoveFromAlbumPressed() async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
 
       bool isSuccess =
           await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum(
@@ -167,7 +168,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
         );
       }
 
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     void handleShareAssets(
@@ -198,9 +199,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
     }
 
     void onShareAssetsTo() async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
       handleShareAssets(ref, context, selected);
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     buildBottomSheetActions() {

+ 1 - 6
mobile/lib/modules/album/ui/album_viewer_editable_title.dart

@@ -78,15 +78,10 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
           borderSide: const BorderSide(color: Colors.transparent),
           borderRadius: BorderRadius.circular(10),
         ),
-        focusColor: Colors.grey[300],
-        fillColor: context.isDarkTheme
-            ? const Color.fromARGB(255, 32, 33, 35)
-            : Colors.grey[200],
         filled: titleFocusNode.hasFocus,
         hintText: 'share_add_title'.tr(),
-        hintStyle: TextStyle(
+        hintStyle: const TextStyle(
           fontSize: 28,
-          color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
           fontWeight: FontWeight.bold,
         ),
       ),

+ 9 - 10
mobile/lib/modules/album/views/album_options_part.dart

@@ -4,6 +4,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/color_extensions.dart';
+import 'package:immich_mobile/extensions/widgetref_extensions.dart';
 import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
@@ -11,7 +13,6 @@ import 'package:immich_mobile/shared/models/album.dart';
 import 'package:immich_mobile/shared/models/user.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
 import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
-import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
 
 class AlbumOptionsPage extends HookConsumerWidget {
   final Album album;
@@ -25,6 +26,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
     final userId = ref.watch(authenticationProvider).userId;
     final activityEnabled = useState(album.activityEnabled);
     final isOwner = owner?.id == userId;
+    final immichOverlayController = ref.useProcessingOverlay();
 
     void showErrorMessage() {
       Navigator.pop(context);
@@ -37,7 +39,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
     }
 
     void leaveAlbum() async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
 
       try {
         final isSuccess =
@@ -54,11 +56,11 @@ class AlbumOptionsPage extends HookConsumerWidget {
         showErrorMessage();
       }
 
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     void removeUserFromAlbum(User user) async {
-      ImmichLoadingOverlayController.appLoader.show();
+      immichOverlayController.value = true;
 
       try {
         await ref
@@ -71,7 +73,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
       }
 
       Navigator.pop(context);
-      ImmichLoadingOverlayController.appLoader.hide();
+      immichOverlayController.value = false;
     }
 
     void handleUserClick(User user) {
@@ -131,7 +133,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
         ),
         subtitle: Text(
           album.owner.value?.email ?? "",
-          style: TextStyle(color: Colors.grey[500]),
+          style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
         ),
         trailing: const Text(
           "Owner",
@@ -162,7 +164,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
             ),
             subtitle: Text(
               user.email,
-              style: TextStyle(color: Colors.grey[500]),
+              style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
             ),
             trailing: userId == user.id || isOwner
                 ? const Icon(Icons.more_horiz_rounded)
@@ -208,9 +210,6 @@ class AlbumOptionsPage extends HookConsumerWidget {
                   album.activityEnabled = value;
                 }
               },
-              activeColor: activityEnabled.value
-                  ? context.primaryColor
-                  : context.themeData.disabledColor,
               dense: true,
               title: Text(
                 "shared_album_activity_setting_title",

+ 6 - 5
mobile/lib/modules/album/views/album_viewer_page.dart

@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/widgetref_extensions.dart';
 import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
 import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
 import 'package:immich_mobile/modules/album/services/album.service.dart';
@@ -19,7 +20,6 @@ import 'package:immich_mobile/shared/models/asset.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
-import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
 
 class AlbumViewerPage extends HookConsumerWidget {
   final int albumId;
@@ -33,6 +33,7 @@ class AlbumViewerPage extends HookConsumerWidget {
     final userId = ref.watch(authenticationProvider).userId;
     final selection = useState<Set<Asset>>({});
     final multiSelectEnabled = useState(false);
+    final immichOverlayController = ref.useProcessingOverlay();
 
     useEffect(
       () {
@@ -78,7 +79,7 @@ class AlbumViewerPage extends HookConsumerWidget {
       if (returnPayload != null) {
         // Check if there is new assets add
         if (returnPayload.selectedAssets.isNotEmpty) {
-          ImmichLoadingOverlayController.appLoader.show();
+          immichOverlayController.value = true;
 
           var addAssetsResult =
               await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
@@ -91,7 +92,7 @@ class AlbumViewerPage extends HookConsumerWidget {
             ref.invalidate(albumDetailProvider(albumId));
           }
 
-          ImmichLoadingOverlayController.appLoader.hide();
+          immichOverlayController.value = false;
         }
       }
     }
@@ -102,7 +103,7 @@ class AlbumViewerPage extends HookConsumerWidget {
       );
 
       if (sharedUserIds != null) {
-        ImmichLoadingOverlayController.appLoader.show();
+        immichOverlayController.value = true;
 
         var isSuccess = await ref
             .watch(albumServiceProvider)
@@ -112,7 +113,7 @@ class AlbumViewerPage extends HookConsumerWidget {
           ref.invalidate(albumDetailProvider(album.id));
         }
 
-        ImmichLoadingOverlayController.appLoader.hide();
+        immichOverlayController.value = false;
       }
     }
 

+ 3 - 4
mobile/lib/modules/album/views/library_page.dart

@@ -101,9 +101,8 @@ class LibraryPage extends HookConsumerWidget {
                   ),
                   Text(
                     option,
-                    style: TextStyle(
+                    style: context.textTheme.displaySmall?.copyWith(
                       color: selected ? context.primaryColor : null,
-                      fontSize: 12.0,
                     ),
                   ),
                 ],
@@ -244,8 +243,8 @@ class LibraryPage extends HookConsumerWidget {
               child: Row(
                 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                 children: [
-                  buildLibraryNavButton(
-                      "library_page_favorites".tr(), Icons.favorite_border, () {
+                  buildLibraryNavButton("library_page_favorites".tr(),
+                      Icons.favorite_outline_rounded, () {
                     context.autoNavigate(const FavoritesRoute());
                   }),
                   const SizedBox(width: 12.0),

+ 8 - 10
mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart

@@ -29,9 +29,11 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
           backgroundColor: context.primaryColor,
-          child: const Icon(
+          radius: 22,
+          child: Icon(
             Icons.check_rounded,
-            size: 25,
+            color: context.colorScheme.surface,
+            size: 24,
           ),
         );
       } else {
@@ -49,14 +51,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
           Padding(
             padding: const EdgeInsets.symmetric(horizontal: 8.0),
             child: Chip(
-              backgroundColor: context.primaryColor.withOpacity(0.15),
+              backgroundColor: context.colorScheme.primaryContainer,
               label: Text(
                 user.email,
-                style: const TextStyle(
-                  fontSize: 12,
-                  color: Colors.black87,
-                  fontWeight: FontWeight.bold,
-                ),
+                style: context.textTheme.displaySmall,
               ),
             ),
           ),
@@ -72,9 +70,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
             padding: const EdgeInsets.all(16.0),
             child: Text(
               'select_additional_user_for_sharing_page_suggestions'.tr(),
-              style: const TextStyle(
+              style: TextStyle(
                 fontSize: 14,
-                color: Colors.grey,
+                color: context.themeData.hintColor,
                 fontWeight: FontWeight.bold,
               ),
             ),

+ 8 - 10
mobile/lib/modules/album/views/select_user_for_sharing_page.dart

@@ -51,9 +51,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
           backgroundColor: context.primaryColor,
-          child: const Icon(
+          radius: 22,
+          child: Icon(
             Icons.check_rounded,
-            size: 25,
+            color: context.colorScheme.surface,
+            size: 24,
           ),
         );
       } else {
@@ -71,14 +73,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
           Padding(
             padding: const EdgeInsets.symmetric(horizontal: 8.0),
             child: Chip(
-              backgroundColor: context.primaryColor.withOpacity(0.15),
+              backgroundColor: context.colorScheme.primaryContainer,
               label: Text(
                 user.email,
-                style: const TextStyle(
-                  fontSize: 12,
-                  color: Colors.black87,
-                  fontWeight: FontWeight.bold,
-                ),
+                style: context.textTheme.displaySmall,
               ),
             ),
           ),
@@ -92,11 +90,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
           ),
           Padding(
             padding: const EdgeInsets.all(16.0),
-            child: const Text(
+            child: Text(
               'select_user_for_sharing_page_share_suggestions',
               style: TextStyle(
                 fontSize: 14,
-                color: Colors.grey,
+                color: context.themeData.hintColor,
                 fontWeight: FontWeight.bold,
               ),
             ).tr(),

+ 3 - 4
mobile/lib/modules/album/views/sharing_page.dart

@@ -81,8 +81,7 @@ class SharingPage extends HookConsumerWidget {
                 overflow: TextOverflow.ellipsis,
                 style: context.textTheme.bodyMedium?.copyWith(
                   fontWeight: FontWeight.bold,
-                  color:
-                      context.isDarkTheme ? context.primaryColor : Colors.black,
+                  color: context.primaryColor,
                 ),
               ),
               subtitle: isOwner
@@ -172,8 +171,8 @@ class SharingPage extends HookConsumerWidget {
             elevation: 0,
             shape: RoundedRectangleBorder(
               borderRadius: BorderRadius.circular(20),
-              side: const BorderSide(
-                color: Colors.grey,
+              side: BorderSide(
+                color: context.themeData.hintColor,
                 width: 0.5,
               ),
             ),

+ 32 - 38
mobile/lib/modules/asset_viewer/ui/advanced_bottom_sheet.dart

@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/shared/models/asset.dart';
+import 'package:immich_mobile/shared/ui/drag_sheet.dart';
 
 class AdvancedBottomSheet extends HookConsumerWidget {
   final Asset assetDetail;
@@ -24,57 +25,50 @@ class AdvancedBottomSheet extends HookConsumerWidget {
         child: Container(
           margin: const EdgeInsets.symmetric(horizontal: 8.0),
           child: LayoutBuilder(
-            builder: (context, constraints) {
+            builder: (ctx, constraints) {
               // One column
               return Column(
-                crossAxisAlignment: CrossAxisAlignment.stretch,
+                crossAxisAlignment: CrossAxisAlignment.center,
                 children: [
-                  const SizedBox(height: 32.0),
-                  const Align(
-                    child: Text(
-                      "ADVANCED INFO",
-                      style: TextStyle(fontSize: 12.0),
+                  const Padding(
+                    padding: EdgeInsets.only(top: 15, bottom: 10),
+                    child: CustomDraggingHandle(),
+                  ),
+                  Padding(
+                    padding: const EdgeInsets.symmetric(vertical: 10),
+                    child: TextButton.icon(
+                      label: Text(
+                        "ADVANCED INFO",
+                        style: context.textTheme.displaySmall,
+                      ),
+                      icon: Icon(
+                        Icons.copy,
+                        size: 16.0,
+                        color: context.primaryColor,
+                      ),
+                      onPressed: () {
+                        Clipboard.setData(
+                          ClipboardData(text: assetDetail.toString()),
+                        ).then((_) {
+                          ScaffoldMessenger.of(ctx).showSnackBar(
+                            const SnackBar(
+                              content: Text("Copied to clipboard"),
+                              behavior: SnackBarBehavior.floating,
+                            ),
+                          );
+                        });
+                      },
                     ),
                   ),
-                  const SizedBox(height: 32.0),
                   Container(
                     decoration: BoxDecoration(
-                      color: context.isDarkTheme
-                          ? Colors.grey[900]
-                          : Colors.grey[200],
                       borderRadius: BorderRadius.circular(15.0),
                     ),
                     child: Padding(
-                      padding: const EdgeInsets.only(
-                        right: 16.0,
-                        left: 16,
-                        top: 8,
-                        bottom: 16,
-                      ),
+                      padding: const EdgeInsets.symmetric(horizontal: 20),
                       child: ListView(
                         shrinkWrap: true,
                         children: [
-                          Align(
-                            alignment: Alignment.centerRight,
-                            child: IconButton(
-                              onPressed: () {
-                                Clipboard.setData(
-                                  ClipboardData(text: assetDetail.toString()),
-                                ).then((_) {
-                                  ScaffoldMessenger.of(context).showSnackBar(
-                                    const SnackBar(
-                                      content: Text("Copied to clipboard"),
-                                    ),
-                                  );
-                                });
-                              },
-                              icon: Icon(
-                                Icons.copy,
-                                size: 16.0,
-                                color: context.primaryColor,
-                              ),
-                            ),
-                          ),
                           SelectableText(
                             assetDetail.toString(),
                             style: const TextStyle(

+ 2 - 3
mobile/lib/modules/asset_viewer/ui/description_input.dart

@@ -20,7 +20,6 @@ class DescriptionInput extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final textColor = context.isDarkTheme ? Colors.white : Colors.black;
     final controller = useTextEditingController();
     final focusNode = useFocusNode();
     final isFocus = useState(false);
@@ -68,7 +67,7 @@ class DescriptionInput extends HookConsumerWidget {
         },
         icon: Icon(
           Icons.cancel_rounded,
-          color: Colors.grey[500],
+          color: context.themeData.hintColor,
         ),
         splashRadius: 10,
       );
@@ -102,7 +101,7 @@ class DescriptionInput extends HookConsumerWidget {
         hintStyle: TextStyle(
           fontWeight: FontWeight.normal,
           fontSize: 12,
-          color: textColor.withOpacity(0.5),
+          color: context.colorScheme.onSurface.withOpacity(0.5),
         ),
         suffixIcon: suffixIcon,
       ),

+ 1 - 1
mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart

@@ -112,7 +112,7 @@ class ExifBottomSheet extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final assetWithExif = ref.watch(assetDetailProvider(asset));
     final exifInfo = (assetWithExif.value ?? asset).exifInfo;
-    var textColor = context.isDarkTheme ? Colors.white : Colors.black;
+    var textColor = context.colorScheme.onSurface;
 
     buildMap() {
       return Padding(

+ 1 - 1
mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart

@@ -61,7 +61,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
     Widget buildAssetInfoTable() {
       return Table(
         border: TableBorder.all(
-          color: context.themeData.primaryColorLight,
+          color: context.colorScheme.onSurfaceVariant,
           width: 1,
         ),
         children: [

+ 3 - 1
mobile/lib/modules/home/ui/control_bottom_app_bar.dart

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/color_extensions.dart';
 import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
 import 'package:immich_mobile/modules/home/models/selection_state.dart';
 import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
@@ -128,7 +129,8 @@ class ControlBottomAppBar extends ConsumerWidget {
         ScrollController scrollController,
       ) {
         return Card(
-          elevation: 18.0,
+          elevation: 2,
+          color: context.colorScheme.surface.lighten(15),
           shape: const RoundedRectangleBorder(
             borderRadius: BorderRadius.only(
               topLeft: Radius.circular(12),

+ 2 - 2
mobile/lib/modules/home/views/home_page.dart

@@ -7,6 +7,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/widgetref_extensions.dart';
 import 'package:immich_mobile/modules/album/providers/album.provider.dart';
 import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
 import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
@@ -49,7 +50,7 @@ class HomePage extends HookConsumerWidget {
 
     final tipOneOpacity = useState(0.0);
     final refreshCount = useState(0);
-    final processing = useState(false);
+    final processing = ref.useProcessingOverlay();
 
     useEffect(
       () {
@@ -363,7 +364,6 @@ class HomePage extends HookConsumerWidget {
                 selectionAssetState: selectionAssetState.value,
                 onStack: onStack,
               ),
-            if (processing.value) const Center(child: ImmichLoadingIndicator()),
           ],
         ),
       );

+ 11 - 6
mobile/lib/modules/map/views/map_page.dart

@@ -125,6 +125,8 @@ class MapPageState extends ConsumerState<MapPage> {
     final refetchMarkers = useState(true);
     final isLoading =
         ref.watch(mapStateNotifier.select((state) => state.isLoading));
+    final mapStyle =
+        ref.watch(mapStateNotifier.select((state) => state.mapStyle));
     final maxZoom = ref.read(mapStateNotifier.notifier).maxZoom;
     final zoomLevel = math.min(maxZoom, 14.0);
     final themeData = isDarkTheme ? immichDarkTheme : immichLightTheme;
@@ -440,7 +442,7 @@ class MapPageState extends ConsumerState<MapPage> {
           extendBodyBehindAppBar: true,
           body: Stack(
             children: [
-              if (!isLoading)
+              if (!isLoading || mapStyle != null)
                 FlutterMap(
                   mapController: mapController,
                   options: MapOptions(
@@ -465,7 +467,7 @@ class MapPageState extends ConsumerState<MapPage> {
                     markerLayer,
                   ],
                 ),
-              if (!isLoading)
+              if (!isLoading || mapStyle != null)
                 MapPageBottomSheet(
                   mapPageEventStream: mapPageEventSC.stream,
                   bottomSheetEventSC: bottomSheetEventSC,
@@ -474,10 +476,13 @@ class MapPageState extends ConsumerState<MapPage> {
                   isDarkTheme: isDarkTheme,
                 ),
               if (showLoadingIndicator.value || isLoading)
-                Positioned(
-                  top: context.height * 0.35,
-                  left: context.width * 0.425,
-                  child: const ImmichLoadingIndicator(),
+                IgnorePointer(
+                  child: Container(
+                    width: double.infinity,
+                    height: double.infinity,
+                    color: context.colorScheme.surface.withAlpha(70),
+                    child: const Center(child: ImmichLoadingIndicator()),
+                  ),
                 ),
             ],
           ),

+ 8 - 4
mobile/lib/modules/settings/ui/settings_switch_list_tile.dart

@@ -35,17 +35,21 @@ class SettingsSwitchListTile extends StatelessWidget {
           onChanged!(value);
         }
       },
-      activeColor:
-          enabled ? context.primaryColor : context.themeData.disabledColor,
+      activeColor: enabled ? null : context.themeData.disabledColor,
       dense: true,
       title: Text(
         title,
-        style:
-            context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
+        style: context.textTheme.labelLarge?.copyWith(
+          fontWeight: FontWeight.bold,
+          color: enabled ? null : context.themeData.disabledColor,
+        ),
       ),
       subtitle: subtitle != null
           ? Text(
               subtitle!,
+              style: TextStyle(
+                color: enabled ? null : context.themeData.disabledColor,
+              ),
             )
           : null,
     );

+ 2 - 4
mobile/lib/modules/trash/views/trash_page.dart

@@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/widgetref_extensions.dart';
 import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
 import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
 import 'package:immich_mobile/modules/trash/providers/trashed_asset.provider.dart';
@@ -11,7 +12,6 @@ import 'package:immich_mobile/shared/models/asset.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/shared/providers/server_info.provider.dart';
 import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
-import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 import 'package:immich_mobile/shared/ui/immich_toast.dart';
 
 class TrashPage extends HookConsumerWidget {
@@ -24,7 +24,7 @@ class TrashPage extends HookConsumerWidget {
         ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
     final selectionEnabledHook = useState(false);
     final selection = useState(<Asset>{});
-    final processing = useState(false);
+    final processing = ref.useProcessingOverlay();
 
     void selectionListener(
       bool multiselect,
@@ -267,8 +267,6 @@ class TrashPage extends HookConsumerWidget {
                     ),
                   ),
                   if (selectionEnabledHook.value) buildBottomBar(),
-                  if (processing.value)
-                    const Center(child: ImmichLoadingIndicator()),
                 ],
               ),
       ),

+ 0 - 8
mobile/lib/routing/tab_navigation_observer.dart

@@ -21,14 +21,6 @@ class TabNavigationObserver extends AutoRouterObserver {
     required this.ref,
   });
 
-  @override
-  void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
-    // Perform tasks on first navigation to SearchRoute
-    if (route.name == 'SearchRoute') {
-      // ref.refresh(getCuratedLocationProvider);
-    }
-  }
-
   @override
   Future<void> didChangeTabRoute(
     TabPageRoute route,

+ 14 - 0
mobile/lib/shared/providers/immich_loading_overlay.provider.dart

@@ -0,0 +1,14 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class ImmichLoadingOverlayNotifier extends StateNotifier<bool> {
+  ImmichLoadingOverlayNotifier() : super(false);
+
+  void show() => state = true;
+
+  void hide() => state = false;
+}
+
+final immichLoadingOverlayController =
+    StateNotifierProvider.autoDispose<ImmichLoadingOverlayNotifier, bool>(
+  (_) => ImmichLoadingOverlayNotifier(),
+);

+ 1 - 1
mobile/lib/shared/ui/confirm_dialog.dart

@@ -44,7 +44,7 @@ class ConfirmDialog extends ConsumerWidget {
           child: Text(
             ok,
             style: TextStyle(
-              color: Colors.red[400],
+              color: context.colorScheme.error,
               fontWeight: FontWeight.bold,
             ),
           ).tr(),

+ 3 - 6
mobile/lib/shared/ui/immich_app_bar.dart

@@ -4,8 +4,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/shared/models/store.dart';
 import 'package:immich_mobile/shared/ui/app_bar_dialog/app_bar_dialog.dart';
 import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
-import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
-import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
@@ -26,7 +24,6 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
     final bool isEnableAutoBackup =
         backupState.backgroundBackup || backupState.autoBackup;
     final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
-    AuthenticationState authState = ref.watch(authenticationProvider);
     final user = Store.tryGet(StoreKey.currentUser);
     const widgetSize = 30.0;
 
@@ -44,9 +41,9 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
               color: Colors.black,
               borderRadius: BorderRadius.circular(widgetSize / 2),
             ),
-            child: const Icon(
+            child: Icon(
               Icons.info,
-              color: Color.fromARGB(255, 243, 188, 106),
+              color: context.orangeColor,
               size: widgetSize / 2,
             ),
           ),
@@ -54,7 +51,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
           alignment: Alignment.bottomRight,
           isLabelVisible: serverInfoState.isVersionMismatch,
           offset: const Offset(2, 2),
-          child: authState.profileImagePath.isEmpty || user == null
+          child: user == null
               ? const Icon(
                   Icons.face_outlined,
                   size: widgetSize,

+ 4 - 4
mobile/lib/shared/ui/immich_loading_indicator.dart

@@ -15,13 +15,13 @@ class ImmichLoadingIndicator extends StatelessWidget {
       height: 60,
       width: 60,
       decoration: BoxDecoration(
-        color: context.primaryColor.withAlpha(200),
+        color: context.primaryColor,
         borderRadius: BorderRadius.circular(borderRadius ?? 10),
       ),
       padding: const EdgeInsets.all(15),
-      child: const CircularProgressIndicator(
-        color: Colors.white,
-        strokeWidth: 2,
+      child: CircularProgressIndicator(
+        color: context.colorScheme.onPrimary,
+        strokeWidth: 3,
       ),
     );
   }

+ 21 - 23
mobile/lib/shared/ui/immich_toast.dart

@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/extensions/color_extensions.dart';
 
 enum ToastType { info, success, error }
 
@@ -9,7 +10,7 @@ class ImmichToast {
     required BuildContext context,
     required String msg,
     ToastType toastType = ToastType.info,
-    ToastGravity gravity = ToastGravity.TOP,
+    ToastGravity gravity = ToastGravity.BOTTOM,
     int durationInSecond = 3,
   }) {
     final fToast = FToast();
@@ -18,7 +19,7 @@ class ImmichToast {
     Color getColor(ToastType type, BuildContext context) {
       switch (type) {
         case ToastType.info:
-          return context.primaryColor;
+          return const Color.fromARGB(255, 48, 111, 220);
         case ToastType.success:
           return const Color.fromARGB(255, 78, 140, 124);
         case ToastType.error:
@@ -26,41 +27,39 @@ class ImmichToast {
       }
     }
 
-    Icon getIcon(ToastType type) {
+    IconData getIcon(ToastType type) {
       switch (type) {
         case ToastType.info:
-          return Icon(
-            Icons.info_outline_rounded,
-            color: context.primaryColor,
-          );
-        case ToastType.success:
-          return const Icon(
-            Icons.check_circle_rounded,
-            color: Color.fromARGB(255, 78, 140, 124),
-          );
+          return Icons.info_outline_rounded;
         case ToastType.error:
-          return const Icon(
-            Icons.error_outline_rounded,
-            color: Color.fromARGB(255, 240, 162, 156),
-          );
+          return Icons.check_circle_outline_rounded;
+        case ToastType.success:
+          return Icons.report_problem_outlined;
       }
     }
 
     fToast.showToast(
       child: Container(
-        padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
+        padding: const EdgeInsets.symmetric(vertical: 12.0),
         decoration: BoxDecoration(
           borderRadius: BorderRadius.circular(5.0),
-          color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[50],
+          color: context.colorScheme.inverseSurface,
           border: Border.all(
-            color: Colors.black12,
+            color: context.colorScheme.inverseSurface,
             width: 1,
           ),
         ),
         child: Row(
-          mainAxisSize: MainAxisSize.min,
+          mainAxisSize: MainAxisSize.max,
+          mainAxisAlignment: MainAxisAlignment.start,
           children: [
-            getIcon(toastType),
+            Padding(
+              padding: const EdgeInsets.only(left: 15, right: 5),
+              child: Icon(
+                getIcon(toastType),
+                color: getColor(toastType, context),
+              ),
+            ),
             const SizedBox(
               width: 12.0,
             ),
@@ -68,8 +67,7 @@ class ImmichToast {
               child: Text(
                 msg,
                 style: TextStyle(
-                  color: getColor(toastType, context),
-                  fontWeight: FontWeight.bold,
+                  color: context.colorScheme.onInverseSurface,
                   fontSize: 15,
                 ),
               ),

+ 1 - 1
mobile/lib/shared/ui/user_circle_avatar.dart

@@ -46,7 +46,7 @@ class UserCircleAvatar extends ConsumerWidget {
       user.firstName[0].toUpperCase(),
       style: TextStyle(
         fontWeight: FontWeight.bold,
-        color: context.isDarkTheme ? Colors.black : Colors.white,
+        color: context.colorScheme.onInverseSurface,
       ),
     );
     return CircleAvatar(

+ 3 - 5
mobile/lib/shared/views/app_log_detail_page.dart

@@ -11,8 +11,6 @@ class AppLogDetailPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    var isDarkTheme = context.isDarkTheme;
-
     buildStackMessage(String stackTrace) {
       return Padding(
         padding: const EdgeInsets.all(8.0),
@@ -53,7 +51,7 @@ class AppLogDetailPage extends HookConsumerWidget {
             ),
             Container(
               decoration: BoxDecoration(
-                color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
+                color: context.colorScheme.surfaceVariant,
                 borderRadius: BorderRadius.circular(15.0),
               ),
               child: Padding(
@@ -112,7 +110,7 @@ class AppLogDetailPage extends HookConsumerWidget {
             ),
             Container(
               decoration: BoxDecoration(
-                color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
+                color: context.colorScheme.surfaceVariant,
                 borderRadius: BorderRadius.circular(15.0),
               ),
               child: Padding(
@@ -151,7 +149,7 @@ class AppLogDetailPage extends HookConsumerWidget {
             ),
             Container(
               decoration: BoxDecoration(
-                color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
+                color: context.colorScheme.surfaceVariant,
                 borderRadius: BorderRadius.circular(15.0),
               ),
               child: Padding(

+ 11 - 15
mobile/lib/shared/views/app_log_page.dart

@@ -16,7 +16,6 @@ class AppLogPage extends HookConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final immichLogger = ImmichLogger();
     final logMessages = useState(immichLogger.messages);
-    final isDarkTheme = context.isDarkTheme;
 
     Widget colorStatusIndicator(Color color) {
       return Column(
@@ -39,12 +38,13 @@ class AppLogPage extends HookConsumerWidget {
         case LogLevel.INFO:
           return colorStatusIndicator(context.primaryColor);
         case LogLevel.SEVERE:
-          return colorStatusIndicator(Colors.redAccent);
-
+          return colorStatusIndicator(context.redColor);
         case LogLevel.WARNING:
-          return colorStatusIndicator(Colors.orangeAccent);
+          return colorStatusIndicator(context.orangeColor);
         default:
-          return colorStatusIndicator(Colors.grey);
+          return colorStatusIndicator(
+            context.colorScheme.onSurfaceVariant.withAlpha(150),
+          );
       }
     }
 
@@ -53,15 +53,11 @@ class AppLogPage extends HookConsumerWidget {
         case LogLevel.INFO:
           return Colors.transparent;
         case LogLevel.SEVERE:
-          return isDarkTheme
-              ? Colors.redAccent.withOpacity(0.25)
-              : Colors.redAccent.withOpacity(0.075);
+          return context.redColor.withAlpha(50);
         case LogLevel.WARNING:
-          return isDarkTheme
-              ? Colors.orangeAccent.withOpacity(0.25)
-              : Colors.orangeAccent.withOpacity(0.075);
+          return context.orangeColor.withAlpha(50);
         default:
-          return context.primaryColor.withOpacity(0.1);
+          return context.primaryColor.withAlpha(50);
       }
     }
 
@@ -116,7 +112,7 @@ class AppLogPage extends HookConsumerWidget {
         separatorBuilder: (context, index) {
           return Divider(
             height: 0,
-            color: isDarkTheme ? Colors.white70 : Colors.grey[600],
+            color: context.themeData.dividerColor,
           );
         },
         itemCount: logMessages.value.length,
@@ -139,7 +135,7 @@ class AppLogPage extends HookConsumerWidget {
                   TextSpan(
                     text: "#$index ",
                     style: TextStyle(
-                      color: isDarkTheme ? Colors.white70 : Colors.grey[600],
+                      color: context.themeData.dividerColor,
                       fontSize: 14.0,
                       fontWeight: FontWeight.bold,
                     ),
@@ -158,7 +154,7 @@ class AppLogPage extends HookConsumerWidget {
               "[${logMessage.context1}] Logged on ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}",
               style: TextStyle(
                 fontSize: 12.0,
-                color: Colors.grey[600],
+                color: context.themeData.hintColor,
               ),
             ),
             leading: buildLeadingIcon(logMessage.level),

+ 61 - 29
mobile/lib/shared/views/immich_loading_overlay.dart

@@ -1,41 +1,73 @@
 import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/shared/providers/immich_loading_overlay.provider.dart';
 import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
+import 'package:immich_mobile/utils/immich_app_theme.dart';
 
-class ImmichLoadingOverlay extends StatelessWidget {
-  const ImmichLoadingOverlay({
-    Key? key,
-  }) : super(key: key);
+class ImmichLoadingOverlay extends StatefulHookConsumerWidget {
+  final ThemeData theme;
+  final ThemeData darkTheme;
+
+  const ImmichLoadingOverlay(
+      {super.key, required this.theme, required this.darkTheme});
 
   @override
-  Widget build(BuildContext context) {
-    return ValueListenableBuilder<bool>(
-      valueListenable:
-          ImmichLoadingOverlayController.appLoader.loaderShowingNotifier,
-      builder: (context, shouldShow, child) {
-        return shouldShow
-            ? const Scaffold(
-                backgroundColor: Colors.black54,
-                body: Center(
-                  child: ImmichLoadingIndicator(),
-                ),
-              )
-            : const SizedBox();
-      },
-    );
-  }
+  ImmichLoadingOverlayState createState() => ImmichLoadingOverlayState();
 }
 
-class ImmichLoadingOverlayController {
-  static final ImmichLoadingOverlayController appLoader =
-      ImmichLoadingOverlayController();
-  ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false);
-  ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');
+class ImmichLoadingOverlayState extends ConsumerState<ImmichLoadingOverlay>
+    with WidgetsBindingObserver {
+  Brightness currentPlatformBrightness =
+      WidgetsBinding.instance.platformDispatcher.platformBrightness;
+
+  @override
+  void initState() {
+    super.initState();
+    WidgetsBinding.instance.addObserver(this);
+  }
+
+  @override
+  void didChangePlatformBrightness() {
+    super.didChangePlatformBrightness();
 
-  void show() {
-    loaderShowingNotifier.value = true;
+    setState(() {
+      currentPlatformBrightness =
+          WidgetsBinding.instance.platformDispatcher.platformBrightness;
+    });
   }
 
-  void hide() {
-    loaderShowingNotifier.value = false;
+  @override
+  void dispose() {
+    WidgetsBinding.instance.removeObserver(this);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final shouldShow = ref.watch(immichLoadingOverlayController);
+
+    final themeMode = ref.watch(immichThemeProvider);
+    final currentBrightness = themeMode == ThemeMode.system
+        ? currentPlatformBrightness
+        : themeMode == ThemeMode.dark
+            ? Brightness.dark
+            : Brightness.light;
+
+    return IgnorePointer(
+      child: shouldShow
+          ? Theme(
+              data: currentBrightness == Brightness.dark
+                  ? widget.darkTheme
+                  : widget.theme,
+              child: Container(
+                width: double.infinity,
+                height: double.infinity,
+                color: context.colorScheme.surface.withAlpha(70),
+                child: const Center(child: ImmichLoadingIndicator()),
+              ),
+            )
+          : const SizedBox(),
+    );
   }
 }

+ 3 - 0
mobile/lib/utils/immich_app_theme.dart

@@ -173,3 +173,6 @@ ThemeData getThemeForScheme(ColorScheme scheme) {
 
 ThemeData immichLightTheme = getThemeForScheme(_lightColorScheme);
 ThemeData immichDarkTheme = getThemeForScheme(_darkColorScheme);
+
+const redAccent = Colors.redAccent;
+const orangeAccent = Colors.orangeAccent;

+ 8 - 0
mobile/pubspec.lock

@@ -321,6 +321,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "7.0.0"
+  dynamic_color:
+    dependency: "direct main"
+    description:
+      name: dynamic_color
+      sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.6.8"
   easy_image_viewer:
     dependency: "direct main"
     description:

+ 1 - 0
mobile/pubspec.yaml

@@ -60,6 +60,7 @@ dependencies:
   wakelock_plus: ^1.1.1
   flutter_local_notifications: ^15.1.0+1
   timezone: ^0.9.2
+  dynamic_color: ^1.6.8
 
   openapi:
     path: openapi