mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	Add mobile dark mode and user setting (#468)
* styling light and dark theme * Icon topbar * Fixed app bar title dark theme * Fixed issue with getting thumbnail for things * Refactor sharing page * Refactor scroll thumb * Refactor chip in auto backup indiation button * Refactor sharing page * Added theme toggle * Up version for testflight build * Refactor backup controller page * Refactor album selection page * refactor album pages * Refactor gradient color profile header * Added theme switcher * Register app theme correctly * Added locale to the app * Added translation key * Styling for bottomsheet colors * up server version * Fixed font size * Fixed overlapsed sliverappbar on photos screen
This commit is contained in:
		| @@ -30,8 +30,8 @@ platform :android do | ||||
|       task: 'bundle',  | ||||
|       build_type: 'Release', | ||||
|       properties: { | ||||
|         "android.injected.version.code" => 32, | ||||
|         "android.injected.version.name" => "1.22.0", | ||||
|         "android.injected.version.code" => 33, | ||||
|         "android.injected.version.name" => "1.23.0", | ||||
|       } | ||||
|     ) | ||||
|     upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') | ||||
|   | ||||
| @@ -48,7 +48,7 @@ | ||||
|   "control_bottom_app_bar_delete": "Delete", | ||||
|   "create_shared_album_page_share": "Share", | ||||
|   "create_shared_album_page_create": "Create", | ||||
|   "create_shared_album_page_share_add_assets": "ADD ASSETS", | ||||
|   "create_shared_album_page_share_add_assets": "ADD PHOTOS", | ||||
|   "create_shared_album_page_share_select_photos": "Select Photos", | ||||
|   "daily_title_text_date": "E, MMM dd", | ||||
|   "daily_title_text_date_year": "E, MMM dd, yyyy", | ||||
| @@ -113,5 +113,14 @@ | ||||
|   "library_page_new_album": "New album", | ||||
|   "create_album_page_untitled": "Untitled", | ||||
|   "share_dialog_preparing": "Preparing...", | ||||
|   "control_bottom_app_bar_share": "Share" | ||||
|   "control_bottom_app_bar_share": "Share", | ||||
|   "setting_pages_app_bar_settings": "Settings", | ||||
|   "theme_setting_theme_title": "Theme", | ||||
|   "theme_setting_theme_subtitle": "Choose the app's theme setting", | ||||
|   "theme_setting_system_theme_switch": "Automatic (Follow system setting)", | ||||
|   "theme_setting_dark_mode_switch": "Dark mode", | ||||
|   "theme_setting_image_viewer_quality_title": "Image viewer quality", | ||||
|   "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", | ||||
|   "theme_setting_three_stage_loading_title": "Enable three-stage loading", | ||||
|   "theme_setting_three_stage_loading_subtitle": "The three-stage loading delivers the best quality image in exchange for a slower loading speed" | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ platform :ios do | ||||
|   desc "iOS Beta" | ||||
|   lane :beta do | ||||
|     increment_version_number( | ||||
|     version_number: "1.22.0" | ||||
|     version_number: "1.23.0" | ||||
|     ) | ||||
|     increment_build_number( | ||||
|       build_number: latest_testflight_build_number + 1, | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| const immichBackgroundColor = Color(0xFFf6f8fe); | ||||
| Color immichBackgroundColor = const Color(0xFFf6f8fe); | ||||
| Color immichDarkBackgroundColor = const Color.fromARGB(255, 0, 0, 0); | ||||
| Color immichDarkThemePrimaryColor = const Color.fromARGB(255, 173, 203, 250); | ||||
|   | ||||
| @@ -7,7 +7,6 @@ import 'package:flutter/services.dart'; | ||||
| import 'package:flutter_displaymode/flutter_displaymode.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | ||||
| import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart'; | ||||
| import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; | ||||
| import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart'; | ||||
| @@ -19,8 +18,10 @@ import 'package:immich_mobile/shared/providers/asset.provider.dart'; | ||||
| import 'package:immich_mobile/shared/providers/release_info.provider.dart'; | ||||
| import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||
| import 'package:immich_mobile/shared/providers/websocket.provider.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
| import 'package:immich_mobile/shared/views/immich_loading_overlay.dart'; | ||||
| import 'package:immich_mobile/shared/views/version_announcement_overlay.dart'; | ||||
| import 'package:immich_mobile/utils/immich_app_theme.dart'; | ||||
| import 'constants/hive_box.dart'; | ||||
|  | ||||
| void main() async { | ||||
| @@ -50,8 +51,11 @@ void main() async { | ||||
|     Locale('da', 'DK'), | ||||
|     Locale('de', 'DE'), | ||||
|     Locale('es', 'ES'), | ||||
|     Locale('fi', 'FI'), | ||||
|     Locale('fr', 'FR'), | ||||
|     Locale('it', 'IT'), | ||||
|     Locale('ja', 'JP'), | ||||
|     Locale('pl', 'PL') | ||||
|   ]; | ||||
|  | ||||
|   if (kReleaseMode && Platform.isAndroid) { | ||||
| @@ -130,7 +134,6 @@ class ImmichAppState extends ConsumerState<ImmichApp> | ||||
|   @override | ||||
|   initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     initApp().then((_) => debugPrint("App Init Completed")); | ||||
|   } | ||||
|  | ||||
| @@ -155,23 +158,9 @@ class ImmichAppState extends ConsumerState<ImmichApp> | ||||
|           MaterialApp.router( | ||||
|             title: 'Immich', | ||||
|             debugShowCheckedModeBanner: false, | ||||
|             theme: ThemeData( | ||||
|               useMaterial3: true, | ||||
|               brightness: Brightness.light, | ||||
|               primarySwatch: Colors.indigo, | ||||
|               fontFamily: 'WorkSans', | ||||
|               snackBarTheme: const SnackBarThemeData( | ||||
|                 contentTextStyle: TextStyle(fontFamily: 'WorkSans'), | ||||
|               ), | ||||
|               scaffoldBackgroundColor: immichBackgroundColor, | ||||
|               appBarTheme: const AppBarTheme( | ||||
|                 backgroundColor: immichBackgroundColor, | ||||
|                 foregroundColor: Colors.indigo, | ||||
|                 elevation: 1, | ||||
|                 centerTitle: true, | ||||
|                 systemOverlayStyle: SystemUiOverlayStyle.dark, | ||||
|               ), | ||||
|             ), | ||||
|             themeMode: ref.watch(immichThemeProvider), | ||||
|             darkTheme: immichDarkTheme, | ||||
|             theme: immichLightTheme, | ||||
|             routeInformationParser: router.defaultRouteParser(), | ||||
|             routerDelegate: router.delegate( | ||||
|               navigatorObservers: () => [TabNavigationObserver(ref: ref)], | ||||
|   | ||||
| @@ -14,6 +14,8 @@ class AlbumActionOutlinedButton extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     return Padding( | ||||
|       padding: const EdgeInsets.only(right: 8.0), | ||||
|       child: OutlinedButton.icon( | ||||
| @@ -22,19 +24,23 @@ class AlbumActionOutlinedButton extends StatelessWidget { | ||||
|           shape: RoundedRectangleBorder( | ||||
|             borderRadius: BorderRadius.circular(25), | ||||
|           ), | ||||
|           side: const BorderSide( | ||||
|           side: BorderSide( | ||||
|             width: 1, | ||||
|             color: Color.fromARGB(255, 215, 215, 215), | ||||
|             color: isDarkTheme | ||||
|                 ? const Color.fromARGB(255, 63, 63, 63) | ||||
|                 : const Color.fromARGB(255, 206, 206, 206), | ||||
|           ), | ||||
|         ), | ||||
|         icon: Icon(iconData, size: 15), | ||||
|         icon: Icon( | ||||
|           iconData, | ||||
|           size: 15, | ||||
|           color: Theme.of(context).primaryColor, | ||||
|         ), | ||||
|         label: Text( | ||||
|           labelText, | ||||
|           style: const TextStyle( | ||||
|             fontSize: 12, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: Colors.black87, | ||||
|           ), | ||||
|           style: Theme.of(context).textTheme.labelSmall?.copyWith( | ||||
|                 fontWeight: FontWeight.bold, | ||||
|               ), | ||||
|         ), | ||||
|         onPressed: onPressed, | ||||
|       ), | ||||
|   | ||||
| @@ -52,7 +52,6 @@ class AlbumThumbnailCard extends StatelessWidget { | ||||
|                   album.albumName, | ||||
|                   style: const TextStyle( | ||||
|                     fontWeight: FontWeight.bold, | ||||
|                     fontSize: 12, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
| @@ -65,14 +64,14 @@ class AlbumThumbnailCard extends StatelessWidget { | ||||
|                       ? 'album_thumbnail_card_item' | ||||
|                       : 'album_thumbnail_card_items', | ||||
|                   style: const TextStyle( | ||||
|                     fontSize: 10, | ||||
|                     fontSize: 12, | ||||
|                   ), | ||||
|                 ).tr(args: ['${album.assetCount}']), | ||||
|                 if (album.shared) | ||||
|                   const Text( | ||||
|                     'album_thumbnail_card_shared', | ||||
|                     style: TextStyle( | ||||
|                       fontSize: 10, | ||||
|                       fontSize: 12, | ||||
|                     ), | ||||
|                   ).tr() | ||||
|               ], | ||||
|   | ||||
| @@ -19,6 +19,8 @@ class AlbumTitleTextField extends ConsumerWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     return TextField( | ||||
|       onChanged: (v) { | ||||
|         if (v.isEmpty) { | ||||
| @@ -51,7 +53,10 @@ class AlbumTitleTextField extends ConsumerWidget { | ||||
|                   albumTitleController.clear(); | ||||
|                   isAlbumTitleEmpty.value = true; | ||||
|                 }, | ||||
|                 icon: const Icon(Icons.cancel_rounded), | ||||
|                 icon: Icon( | ||||
|                   Icons.cancel_rounded, | ||||
|                   color: Theme.of(context).primaryColor, | ||||
|                 ), | ||||
|                 splashRadius: 10, | ||||
|               ) | ||||
|             : null, | ||||
| @@ -65,7 +70,9 @@ class AlbumTitleTextField extends ConsumerWidget { | ||||
|         ), | ||||
|         hintText: 'share_add_title'.tr(), | ||||
|         focusColor: Colors.grey[300], | ||||
|         fillColor: Colors.grey[200], | ||||
|         fillColor: isDarkTheme | ||||
|             ? const Color.fromARGB(255, 32, 33, 35) | ||||
|             : Colors.grey[200], | ||||
|         filled: isAlbumTitleTextFieldFocus.value, | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -150,7 +150,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget { | ||||
|  | ||||
|     void _buildBottomSheet() { | ||||
|       showModalBottomSheet( | ||||
|         backgroundColor: immichBackgroundColor, | ||||
|         backgroundColor: Theme.of(context).scaffoldBackgroundColor, | ||||
|         isScrollControlled: false, | ||||
|         context: context, | ||||
|         builder: (context) { | ||||
|   | ||||
| @@ -18,6 +18,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final titleTextEditController = | ||||
|         useTextEditingController(text: albumInfo.albumName); | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     void onFocusModeChange() { | ||||
|       if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) { | ||||
| @@ -65,7 +66,10 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { | ||||
|                 onPressed: () { | ||||
|                   titleTextEditController.clear(); | ||||
|                 }, | ||||
|                 icon: const Icon(Icons.cancel_rounded), | ||||
|                 icon: Icon( | ||||
|                   Icons.cancel_rounded, | ||||
|                   color: Theme.of(context).primaryColor, | ||||
|                 ), | ||||
|                 splashRadius: 10, | ||||
|               ) | ||||
|             : null, | ||||
| @@ -78,7 +82,9 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { | ||||
|           borderRadius: BorderRadius.circular(10), | ||||
|         ), | ||||
|         focusColor: Colors.grey[300], | ||||
|         fillColor: Colors.grey[200], | ||||
|         fillColor: isDarkTheme | ||||
|             ? const Color.fromARGB(255, 32, 33, 35) | ||||
|             : Colors.grey[200], | ||||
|         filled: titleFocusNode.hasFocus, | ||||
|         hintText: 'share_add_title'.tr(), | ||||
|       ), | ||||
|   | ||||
| @@ -35,13 +35,7 @@ class SharingSliverAppBar extends StatelessWidget { | ||||
|               Expanded( | ||||
|                 child: Padding( | ||||
|                   padding: const EdgeInsets.only(right: 4.0), | ||||
|                   child: TextButton.icon( | ||||
|                     style: ButtonStyle( | ||||
|                       backgroundColor: MaterialStateProperty.all( | ||||
|                         Theme.of(context).primaryColor.withAlpha(20), | ||||
|                       ), | ||||
|                       // foregroundColor: MaterialStateProperty.all(Colors.white), | ||||
|                     ), | ||||
|                   child: ElevatedButton.icon( | ||||
|                     onPressed: () { | ||||
|                       AutoRouter.of(context) | ||||
|                           .push(CreateAlbumRoute(isSharedAlbum: true)); | ||||
| @@ -52,8 +46,12 @@ class SharingSliverAppBar extends StatelessWidget { | ||||
|                     ), | ||||
|                     label: const Text( | ||||
|                       "sharing_silver_appbar_create_shared_album", | ||||
|                       style: | ||||
|                           TextStyle(fontWeight: FontWeight.bold, fontSize: 12), | ||||
|                       maxLines: 1, | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.bold, | ||||
|                         fontSize: 11, | ||||
|                         // color: Theme.of(context).primaryColor, | ||||
|                       ), | ||||
|                     ).tr(), | ||||
|                   ), | ||||
|                 ), | ||||
| @@ -61,13 +59,7 @@ class SharingSliverAppBar extends StatelessWidget { | ||||
|               Expanded( | ||||
|                 child: Padding( | ||||
|                   padding: const EdgeInsets.only(left: 4.0), | ||||
|                   child: TextButton.icon( | ||||
|                     style: ButtonStyle( | ||||
|                       backgroundColor: MaterialStateProperty.all( | ||||
|                         Theme.of(context).primaryColor.withAlpha(20), | ||||
|                       ), | ||||
|                       // foregroundColor: MaterialStateProperty.all(Colors.white), | ||||
|                     ), | ||||
|                   child: ElevatedButton.icon( | ||||
|                     onPressed: null, | ||||
|                     icon: const Icon( | ||||
|                       Icons.swap_horizontal_circle_outlined, | ||||
| @@ -75,8 +67,11 @@ class SharingSliverAppBar extends StatelessWidget { | ||||
|                     ), | ||||
|                     label: const Text( | ||||
|                       "sharing_silver_appbar_share_partner", | ||||
|                       style: | ||||
|                           TextStyle(fontWeight: FontWeight.bold, fontSize: 12), | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.bold, | ||||
|                         fontSize: 11, | ||||
|                       ), | ||||
|                       maxLines: 1, | ||||
|                     ).tr(), | ||||
|                   ), | ||||
|                 ), | ||||
|   | ||||
| @@ -3,7 +3,6 @@ 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/modules/home/ui/draggable_scrollbar.dart'; | ||||
| import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | ||||
| import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart'; | ||||
| @@ -242,7 +241,7 @@ class AlbumViewerPage extends HookConsumerWidget { | ||||
|           titleFocusNode.unfocus(); | ||||
|         }, | ||||
|         child: DraggableScrollbar.semicircle( | ||||
|           backgroundColor: Theme.of(context).primaryColor, | ||||
|           backgroundColor: Theme.of(context).hintColor, | ||||
|           controller: scrollController, | ||||
|           heightScrollThumb: 48.0, | ||||
|           child: CustomScrollView( | ||||
| @@ -255,7 +254,7 @@ class AlbumViewerPage extends HookConsumerWidget { | ||||
|                   minHeight: 50, | ||||
|                   maxHeight: 50, | ||||
|                   child: Container( | ||||
|                     color: immichBackgroundColor, | ||||
|                     color: Theme.of(context).scaffoldBackgroundColor, | ||||
|                     child: _buildControlButton(albumInfo), | ||||
|                   ), | ||||
|                 ), | ||||
|   | ||||
| @@ -43,7 +43,7 @@ class AssetSelectionPage extends HookConsumerWidget { | ||||
|       return Stack( | ||||
|         children: [ | ||||
|           DraggableScrollbar.semicircle( | ||||
|             backgroundColor: Theme.of(context).primaryColor, | ||||
|             backgroundColor: Theme.of(context).hintColor, | ||||
|             controller: scrollController, | ||||
|             heightScrollThumb: 48.0, | ||||
|             child: CustomScrollView( | ||||
|   | ||||
| @@ -27,6 +27,7 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|     final isAlbumTitleEmpty = useState(true); | ||||
|     final selectedAssets = | ||||
|         ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum; | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     _showSelectUserPage() { | ||||
|       AutoRouter.of(context).push(const SelectUserForSharingRoute()); | ||||
| @@ -75,9 +76,12 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|         return SliverToBoxAdapter( | ||||
|           child: Padding( | ||||
|             padding: const EdgeInsets.only(top: 200, left: 18), | ||||
|             child: const Text( | ||||
|             child: Text( | ||||
|               'create_shared_album_page_share_add_assets', | ||||
|               style: TextStyle(fontSize: 12), | ||||
|               style: Theme.of(context).textTheme.headline2?.copyWith( | ||||
|                     fontSize: 12, | ||||
|                     fontWeight: FontWeight.normal, | ||||
|                   ), | ||||
|             ).tr(), | ||||
|           ), | ||||
|         ); | ||||
| @@ -96,24 +100,28 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|                 alignment: Alignment.centerLeft, | ||||
|                 padding: | ||||
|                     const EdgeInsets.symmetric(vertical: 22, horizontal: 16), | ||||
|                 side: const BorderSide( | ||||
|                   color: Color.fromARGB(255, 206, 206, 206), | ||||
|                 side: BorderSide( | ||||
|                   color: isDarkTheme | ||||
|                       ? const Color.fromARGB(255, 63, 63, 63) | ||||
|                       : const Color.fromARGB(255, 206, 206, 206), | ||||
|                 ), | ||||
|                 shape: RoundedRectangleBorder( | ||||
|                   borderRadius: BorderRadius.circular(5), | ||||
|                 ), | ||||
|               ), | ||||
|               onPressed: _onSelectPhotosButtonPressed, | ||||
|               icon: const Icon(Icons.add_rounded), | ||||
|               icon: Icon( | ||||
|                 Icons.add_rounded, | ||||
|                 color: Theme.of(context).primaryColor, | ||||
|               ), | ||||
|               label: Padding( | ||||
|                 padding: const EdgeInsets.only(left: 8.0), | ||||
|                 child: Text( | ||||
|                   'create_shared_album_page_share_select_photos', | ||||
|                   style: TextStyle( | ||||
|                     fontSize: 16, | ||||
|                     color: Colors.grey[700], | ||||
|                     fontWeight: FontWeight.bold, | ||||
|                   ), | ||||
|                   style: Theme.of(context).textTheme.labelLarge?.copyWith( | ||||
|                         fontSize: 16, | ||||
|                         fontWeight: FontWeight.bold, | ||||
|                       ), | ||||
|                 ).tr(), | ||||
|               ), | ||||
|             ), | ||||
| @@ -190,6 +198,7 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|       appBar: AppBar( | ||||
|         elevation: 0, | ||||
|         centerTitle: false, | ||||
|         backgroundColor: Theme.of(context).scaffoldBackgroundColor, | ||||
|         leading: IconButton( | ||||
|           onPressed: () { | ||||
|             ref.watch(assetSelectionProvider.notifier).removeAll(); | ||||
| @@ -197,9 +206,11 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|           }, | ||||
|           icon: const Icon(Icons.close_rounded), | ||||
|         ), | ||||
|         title: const Text( | ||||
|         title: Text( | ||||
|           'share_create_album', | ||||
|           style: TextStyle(color: Colors.black), | ||||
|           style: Theme.of(context).textTheme.headline2?.copyWith( | ||||
|                 color: Theme.of(context).primaryColor, | ||||
|               ), | ||||
|         ).tr(), | ||||
|         actions: [ | ||||
|           if (isSharedAlbum) | ||||
| @@ -209,8 +220,9 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|                   : null, | ||||
|               child: Text( | ||||
|                 'create_shared_album_page_share'.tr(), | ||||
|                 style: const TextStyle( | ||||
|                 style: TextStyle( | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   color: Theme.of(context).primaryColor, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
| @@ -234,9 +246,9 @@ class CreateAlbumPage extends HookConsumerWidget { | ||||
|         child: CustomScrollView( | ||||
|           slivers: [ | ||||
|             SliverAppBar( | ||||
|               backgroundColor: Theme.of(context).scaffoldBackgroundColor, | ||||
|               elevation: 5, | ||||
|               automaticallyImplyLeading: false, | ||||
|               // leading: Container(), | ||||
|               pinned: true, | ||||
|               floating: false, | ||||
|               bottom: PreferredSize( | ||||
|   | ||||
| @@ -23,7 +23,7 @@ class LibraryPage extends HookConsumerWidget { | ||||
|     ); | ||||
|  | ||||
|     Widget _buildAppBar() { | ||||
|       return SliverAppBar( | ||||
|       return const SliverAppBar( | ||||
|         centerTitle: true, | ||||
|         floating: true, | ||||
|         pinned: false, | ||||
| @@ -35,7 +35,6 @@ class LibraryPage extends HookConsumerWidget { | ||||
|             fontFamily: 'SnowburstOne', | ||||
|             fontWeight: FontWeight.bold, | ||||
|             fontSize: 22, | ||||
|             color: Theme.of(context).primaryColor, | ||||
|           ), | ||||
|         ), | ||||
|       ); | ||||
| @@ -72,7 +71,6 @@ class LibraryPage extends HookConsumerWidget { | ||||
|               child: const Text( | ||||
|                 'library_page_new_album', | ||||
|                 style: TextStyle( | ||||
|                   fontSize: 12, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                 ), | ||||
|               ).tr(), | ||||
|   | ||||
| @@ -136,9 +136,9 @@ class SelectUserForSharingPage extends HookConsumerWidget { | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text( | ||||
|         title: Text( | ||||
|           'share_invite', | ||||
|           style: TextStyle(color: Colors.black), | ||||
|           style: TextStyle(color: Theme.of(context).primaryColor), | ||||
|         ).tr(), | ||||
|         elevation: 0, | ||||
|         centerTitle: false, | ||||
| @@ -150,11 +150,18 @@ class SelectUserForSharingPage extends HookConsumerWidget { | ||||
|         ), | ||||
|         actions: [ | ||||
|           TextButton( | ||||
|             style: TextButton.styleFrom( | ||||
|               primary: Theme.of(context).primaryColor, | ||||
|             ), | ||||
|             onPressed: | ||||
|                 sharedUsersList.value.isEmpty ? null : _createSharedAlbum, | ||||
|             child: const Text( | ||||
|               "share_create_album", | ||||
|               style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), | ||||
|               style: TextStyle( | ||||
|                 fontSize: 14, | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 // color: Theme.of(context).primaryColor, | ||||
|               ), | ||||
|             ).tr(), | ||||
|           ) | ||||
|         ], | ||||
|   | ||||
| @@ -61,11 +61,9 @@ class SharingPage extends HookConsumerWidget { | ||||
|                 sharedAlbums[index].albumName, | ||||
|                 maxLines: 1, | ||||
|                 overflow: TextOverflow.ellipsis, | ||||
|                 style: TextStyle( | ||||
|                   fontSize: 16, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   color: Colors.grey.shade800, | ||||
|                 ), | ||||
|                 style: Theme.of(context).textTheme.bodyMedium?.copyWith( | ||||
|                       fontWeight: FontWeight.bold, | ||||
|                     ), | ||||
|               ), | ||||
|               onTap: () { | ||||
|                 AutoRouter.of(context) | ||||
| @@ -87,7 +85,7 @@ class SharingPage extends HookConsumerWidget { | ||||
|             shape: RoundedRectangleBorder( | ||||
|               borderRadius: BorderRadius.circular(10), // if you need this | ||||
|               side: const BorderSide( | ||||
|                 color: Colors.black12, | ||||
|                 color: Colors.grey, | ||||
|                 width: 1, | ||||
|               ), | ||||
|             ), | ||||
| @@ -97,30 +95,26 @@ class SharingPage extends HookConsumerWidget { | ||||
|               child: Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   Padding( | ||||
|                     padding: const EdgeInsets.only(left: 5.0, bottom: 5), | ||||
|                   const Padding( | ||||
|                     padding: EdgeInsets.only(left: 5.0, bottom: 5), | ||||
|                     child: Icon( | ||||
|                       Icons.offline_share_outlined, | ||||
|                       size: 50, | ||||
|                       color: Theme.of(context).primaryColor.withAlpha(200), | ||||
|                       // color: Theme.of(context).primaryColor, | ||||
|                     ), | ||||
|                   ), | ||||
|                   Padding( | ||||
|                     padding: const EdgeInsets.all(8.0), | ||||
|                     child: Text( | ||||
|                       'sharing_page_empty_list', | ||||
|                       style: TextStyle( | ||||
|                         fontSize: 12, | ||||
|                         color: Theme.of(context).primaryColor, | ||||
|                         fontWeight: FontWeight.bold, | ||||
|                       ), | ||||
|                       style: Theme.of(context).textTheme.headline3, | ||||
|                     ).tr(), | ||||
|                   ), | ||||
|                   Padding( | ||||
|                     padding: const EdgeInsets.all(8.0), | ||||
|                     child: Text( | ||||
|                       'sharing_page_description', | ||||
|                       style: TextStyle(fontSize: 12, color: Colors.grey[700]), | ||||
|                       style: Theme.of(context).textTheme.bodyMedium, | ||||
|                     ).tr(), | ||||
|                   ), | ||||
|                 ], | ||||
|   | ||||
| @@ -11,7 +11,8 @@ import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; | ||||
| import 'package:immich_mobile/modules/asset_viewer/views/image_viewer_page.dart'; | ||||
| import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; | ||||
| import 'package:immich_mobile/modules/home/services/asset.service.dart'; | ||||
| import 'package:immich_mobile/shared/services/app_settings.service.dart'; | ||||
| import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
| import 'package:openapi/api.dart'; | ||||
|  | ||||
| // ignore: must_be_immutable | ||||
|   | ||||
| @@ -24,6 +24,7 @@ class AlbumInfoCard extends HookConsumerWidget { | ||||
|         ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo); | ||||
|     final bool isExcluded = | ||||
|         ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     ColorFilter selectedFilter = ColorFilter.mode( | ||||
|       Theme.of(context).primaryColor.withAlpha(100), | ||||
| @@ -39,11 +40,11 @@ class AlbumInfoCard extends HookConsumerWidget { | ||||
|         return Chip( | ||||
|           visualDensity: VisualDensity.compact, | ||||
|           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), | ||||
|           label: const Text( | ||||
|           label: Text( | ||||
|             "album_info_card_backup_album_included", | ||||
|             style: TextStyle( | ||||
|               fontSize: 10, | ||||
|               color: Colors.white, | ||||
|               color: isDarkTheme ? Colors.black : Colors.white, | ||||
|               fontWeight: FontWeight.bold, | ||||
|             ), | ||||
|           ).tr(), | ||||
| @@ -53,11 +54,11 @@ class AlbumInfoCard extends HookConsumerWidget { | ||||
|         return Chip( | ||||
|           visualDensity: VisualDensity.compact, | ||||
|           shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), | ||||
|           label: const Text( | ||||
|           label: Text( | ||||
|             "album_info_card_backup_album_excluded", | ||||
|             style: TextStyle( | ||||
|               fontSize: 10, | ||||
|               color: Colors.white, | ||||
|               color: isDarkTheme ? Colors.black : Colors.white, | ||||
|               fontWeight: FontWeight.bold, | ||||
|             ), | ||||
|           ).tr(), | ||||
| @@ -141,8 +142,10 @@ class AlbumInfoCard extends HookConsumerWidget { | ||||
|         margin: const EdgeInsets.all(1), | ||||
|         shape: RoundedRectangleBorder( | ||||
|           borderRadius: BorderRadius.circular(12), // if you need this | ||||
|           side: const BorderSide( | ||||
|             color: Color(0xFFC9C9C9), | ||||
|           side: BorderSide( | ||||
|             color: isDarkTheme | ||||
|                 ? const Color.fromARGB(255, 37, 35, 35) | ||||
|                 : const Color(0xFFC9C9C9), | ||||
|             width: 1, | ||||
|           ), | ||||
|         ), | ||||
| @@ -219,8 +222,9 @@ class AlbumInfoCard extends HookConsumerWidget { | ||||
|                   ), | ||||
|                   IconButton( | ||||
|                     onPressed: () { | ||||
|                       AutoRouter.of(context) | ||||
|                           .push(AlbumPreviewRoute(album: albumInfo)); | ||||
|                       AutoRouter.of(context).push( | ||||
|                         AlbumPreviewRoute(album: albumInfo), | ||||
|                       ); | ||||
|                     }, | ||||
|                     icon: Icon( | ||||
|                       Icons.image_outlined, | ||||
|   | ||||
| @@ -35,7 +35,7 @@ class BackupInfoCard extends StatelessWidget { | ||||
|           padding: const EdgeInsets.only(top: 8.0), | ||||
|           child: Text( | ||||
|             subtitle, | ||||
|             style: const TextStyle(color: Color(0xFF808080), fontSize: 12), | ||||
|             style: const TextStyle(fontSize: 12), | ||||
|           ), | ||||
|         ), | ||||
|         trailing: Column( | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:fluttertoast/fluttertoast.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | ||||
| import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; | ||||
| import 'package:immich_mobile/modules/backup/ui/album_info_card.dart'; | ||||
| import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; | ||||
| @@ -16,6 +17,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|     final availableAlbums = ref.watch(backupProvider).availableAlbums; | ||||
|     final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; | ||||
|     final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; | ||||
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     useEffect( | ||||
|       () { | ||||
| @@ -81,14 +83,16 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|               ), | ||||
|               label: Text( | ||||
|                 album.name, | ||||
|                 style: const TextStyle( | ||||
|                 style: TextStyle( | ||||
|                   fontSize: 10, | ||||
|                   color: Colors.white, | ||||
|                   color: Theme.of(context).brightness == Brightness.dark | ||||
|                       ? Colors.black | ||||
|                       : Colors.white, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                 ), | ||||
|               ), | ||||
|               backgroundColor: Theme.of(context).primaryColor, | ||||
|               deleteIconColor: Colors.white, | ||||
|               deleteIconColor: isDarkTheme ? Colors.black : Colors.white, | ||||
|               deleteIcon: const Icon( | ||||
|                 Icons.cancel_rounded, | ||||
|                 size: 15, | ||||
| @@ -119,14 +123,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|               ), | ||||
|               label: Text( | ||||
|                 album.name, | ||||
|                 style: const TextStyle( | ||||
|                 style: TextStyle( | ||||
|                   fontSize: 10, | ||||
|                   color: Colors.white, | ||||
|                   color: isDarkTheme ? Colors.black : immichBackgroundColor, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                 ), | ||||
|               ), | ||||
|               backgroundColor: Colors.red[300], | ||||
|               deleteIconColor: Colors.white, | ||||
|               deleteIconColor: | ||||
|                   isDarkTheme ? Colors.black : immichBackgroundColor, | ||||
|               deleteIcon: const Icon( | ||||
|                 Icons.cancel_rounded, | ||||
|                 size: 15, | ||||
| @@ -154,11 +159,16 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|         physics: const ClampingScrollPhysics(), | ||||
|         children: [ | ||||
|           Padding( | ||||
|             padding: | ||||
|                 const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), | ||||
|             padding: const EdgeInsets.symmetric( | ||||
|               vertical: 8.0, | ||||
|               horizontal: 16.0, | ||||
|             ), | ||||
|             child: const Text( | ||||
|               "backup_album_selection_page_selection_info", | ||||
|               style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), | ||||
|               style: TextStyle( | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 fontSize: 14, | ||||
|               ), | ||||
|             ).tr(), | ||||
|           ), | ||||
|           // Selected Album Chips | ||||
| @@ -178,9 +188,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|             child: Card( | ||||
|               margin: const EdgeInsets.all(0), | ||||
|               shape: RoundedRectangleBorder( | ||||
|                 borderRadius: BorderRadius.circular(5), // if you need this | ||||
|                 side: const BorderSide( | ||||
|                   color: Color.fromARGB(255, 235, 235, 235), | ||||
|                 borderRadius: BorderRadius.circular(5), | ||||
|                 side: BorderSide( | ||||
|                   color: isDarkTheme | ||||
|                       ? const Color.fromARGB(255, 0, 0, 0) | ||||
|                       : const Color.fromARGB(255, 235, 235, 235), | ||||
|                   width: 1, | ||||
|                 ), | ||||
|               ), | ||||
| @@ -190,12 +202,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|                 children: [ | ||||
|                   ListTile( | ||||
|                     visualDensity: VisualDensity.compact, | ||||
|                     title: Text( | ||||
|                     title: const Text( | ||||
|                       "backup_album_selection_page_total_assets", | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.bold, | ||||
|                         fontSize: 14, | ||||
|                         color: Colors.grey[700], | ||||
|                       ), | ||||
|                     ).tr(), | ||||
|                     trailing: Text( | ||||
| @@ -257,11 +268,10 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { | ||||
|                       content: SingleChildScrollView( | ||||
|                         child: ListBody( | ||||
|                           children: [ | ||||
|                             Text( | ||||
|                             const Text( | ||||
|                               'backup_album_selection_page_assets_scatter', | ||||
|                               style: TextStyle( | ||||
|                                 fontSize: 14, | ||||
|                                 color: Colors.grey[700], | ||||
|                               ), | ||||
|                             ).tr(), | ||||
|                           ], | ||||
|   | ||||
| @@ -82,7 +82,7 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     ListTile _buildBackupController() { | ||||
|     ListTile _buildAutoBackupController() { | ||||
|       var backUpOption = authenticationState.deviceInfo.isAutoBackup | ||||
|           ? "backup_controller_page_status_on".tr() | ||||
|           : "backup_controller_page_status_off".tr(); | ||||
| @@ -114,13 +114,7 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                 ).tr(), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.only(top: 8.0), | ||||
|                 child: OutlinedButton( | ||||
|                   style: OutlinedButton.styleFrom( | ||||
|                     side: const BorderSide( | ||||
|                       width: 1, | ||||
|                       color: Color.fromARGB(255, 220, 220, 220), | ||||
|                     ), | ||||
|                   ), | ||||
|                 child: ElevatedButton( | ||||
|                   onPressed: () { | ||||
|                     if (isAutoBackup) { | ||||
|                       ref | ||||
| @@ -134,7 +128,10 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                   }, | ||||
|                   child: Text( | ||||
|                     backupBtnText, | ||||
|                     style: const TextStyle(fontWeight: FontWeight.bold), | ||||
|                     style: const TextStyle( | ||||
|                       fontWeight: FontWeight.bold, | ||||
|                       fontSize: 12, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ) | ||||
| @@ -232,33 +229,24 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|               children: [ | ||||
|                 const Text( | ||||
|                   "backup_controller_page_to_backup", | ||||
|                   style: TextStyle(color: Color(0xFF808080), fontSize: 12), | ||||
|                   style: TextStyle(fontSize: 12), | ||||
|                 ).tr(), | ||||
|                 _buildSelectedAlbumName(), | ||||
|                 _buildExcludedAlbumName() | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           trailing: OutlinedButton( | ||||
|             style: OutlinedButton.styleFrom( | ||||
|               enableFeedback: true, | ||||
|               side: const BorderSide( | ||||
|                 width: 1, | ||||
|                 color: Color.fromARGB(255, 220, 220, 220), | ||||
|               ), | ||||
|             ), | ||||
|           trailing: ElevatedButton( | ||||
|             onPressed: () { | ||||
|               AutoRouter.of(context).push(const BackupAlbumSelectionRoute()); | ||||
|             }, | ||||
|             child: Padding( | ||||
|               padding: const EdgeInsets.symmetric( | ||||
|                 vertical: 16.0, | ||||
|             child: const Text( | ||||
|               "backup_controller_page_select", | ||||
|               style: TextStyle( | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 fontSize: 12, | ||||
|               ), | ||||
|               child: const Text( | ||||
|                 "backup_controller_page_select", | ||||
|                 style: TextStyle(fontWeight: FontWeight.bold), | ||||
|               ).tr(), | ||||
|             ), | ||||
|             ).tr(), | ||||
|           ), | ||||
|         ), | ||||
|       ); | ||||
| @@ -324,14 +312,14 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|               padding: const EdgeInsets.only(top: 8.0), | ||||
|               child: Table( | ||||
|                 border: TableBorder.all( | ||||
|                   color: Colors.black12, | ||||
|                   color: Theme.of(context).primaryColorLight, | ||||
|                   width: 1, | ||||
|                 ), | ||||
|                 children: [ | ||||
|                   TableRow( | ||||
|                     decoration: BoxDecoration( | ||||
|                       color: Colors.grey[100], | ||||
|                     ), | ||||
|                     decoration: const BoxDecoration( | ||||
|                         // color: Colors.grey[100], | ||||
|                         ), | ||||
|                     children: [ | ||||
|                       TableCell( | ||||
|                         verticalAlignment: TableCellVerticalAlignment.middle, | ||||
| @@ -355,9 +343,9 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                     ], | ||||
|                   ), | ||||
|                   TableRow( | ||||
|                     decoration: BoxDecoration( | ||||
|                       color: Colors.grey[200], | ||||
|                     ), | ||||
|                     decoration: const BoxDecoration( | ||||
|                         // color: Colors.grey[200], | ||||
|                         ), | ||||
|                     children: [ | ||||
|                       TableCell( | ||||
|                         verticalAlignment: TableCellVerticalAlignment.middle, | ||||
| @@ -384,9 +372,9 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                     ], | ||||
|                   ), | ||||
|                   TableRow( | ||||
|                     decoration: BoxDecoration( | ||||
|                       color: Colors.grey[100], | ||||
|                     ), | ||||
|                     decoration: const BoxDecoration( | ||||
|                         // color: Colors.grey[100], | ||||
|                         ), | ||||
|                     children: [ | ||||
|                       TableCell( | ||||
|                         child: Padding( | ||||
| @@ -463,7 +451,7 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                   "${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}", | ||||
|             ), | ||||
|             const Divider(), | ||||
|             _buildBackupController(), | ||||
|             _buildAutoBackupController(), | ||||
|             const Divider(), | ||||
|             _buildStorageInformation(), | ||||
|             const Divider(), | ||||
| @@ -479,7 +467,7 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                             style: ElevatedButton.styleFrom( | ||||
|                               primary: Colors.red[300], | ||||
|                               onPrimary: Colors.grey[50], | ||||
|                               padding: const EdgeInsets.all(14), | ||||
|                               // padding: const EdgeInsets.all(14), | ||||
|                             ), | ||||
|                             onPressed: () { | ||||
|                               ref.read(backupProvider.notifier).cancelBackup(); | ||||
| @@ -493,11 +481,6 @@ class BackupControllerPage extends HookConsumerWidget { | ||||
|                             ).tr(), | ||||
|                           ) | ||||
|                         : ElevatedButton( | ||||
|                             style: ElevatedButton.styleFrom( | ||||
|                               primary: Theme.of(context).primaryColor, | ||||
|                               onPrimary: Colors.grey[50], | ||||
|                               padding: const EdgeInsets.all(14), | ||||
|                             ), | ||||
|                             onPressed: shouldBackup ? startBackup : null, | ||||
|                             child: const Text( | ||||
|                               "backup_controller_page_start_backup", | ||||
|   | ||||
| @@ -1,11 +1,9 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart'; | ||||
|  | ||||
| import '../../../shared/providers/asset.provider.dart'; | ||||
| import '../providers/home_page_state.provider.dart'; | ||||
|  | ||||
| class ControlBottomAppBar extends ConsumerWidget { | ||||
|   const ControlBottomAppBar({Key? key}) : super(key: key); | ||||
|  | ||||
| @@ -19,10 +17,10 @@ class ControlBottomAppBar extends ConsumerWidget { | ||||
|         height: MediaQuery.of(context).size.height * 0.15, | ||||
|         decoration: BoxDecoration( | ||||
|           borderRadius: const BorderRadius.only( | ||||
|             topLeft: Radius.circular(15), | ||||
|             topRight: Radius.circular(15), | ||||
|             topLeft: Radius.circular(8), | ||||
|             topRight: Radius.circular(8), | ||||
|           ), | ||||
|           color: Colors.grey[300]?.withOpacity(0.98), | ||||
|           color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.95), | ||||
|         ), | ||||
|         child: Column( | ||||
|           children: [ | ||||
|   | ||||
| @@ -86,7 +86,6 @@ class DailyTitleText extends ConsumerWidget { | ||||
|               style: const TextStyle( | ||||
|                 fontSize: 14, | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 color: Colors.black87, | ||||
|               ), | ||||
|             ), | ||||
|             const Spacer(), | ||||
|   | ||||
| @@ -14,32 +14,22 @@ class DisableMultiSelectButton extends ConsumerWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     return Positioned( | ||||
|       top: 0, | ||||
|       top: 10, | ||||
|       left: 0, | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(left: 16.0, top: 46), | ||||
|         child: Material( | ||||
|           elevation: 20, | ||||
|           borderRadius: BorderRadius.circular(35), | ||||
|           child: Container( | ||||
|             decoration: BoxDecoration( | ||||
|               borderRadius: BorderRadius.circular(35), | ||||
|               color: Colors.grey[100], | ||||
|             ), | ||||
|             child: Padding( | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 4.0), | ||||
|               child: TextButton.icon( | ||||
|                 onPressed: () { | ||||
|                   onPressed(); | ||||
|                 }, | ||||
|                 icon: const Icon(Icons.close_rounded), | ||||
|                 label: Text( | ||||
|                   '$selectedItemCount', | ||||
|                   style: const TextStyle( | ||||
|                     fontWeight: FontWeight.w600, | ||||
|                     fontSize: 18, | ||||
|                   ), | ||||
|                 ), | ||||
|         child: Padding( | ||||
|           padding: const EdgeInsets.symmetric(horizontal: 4.0), | ||||
|           child: ElevatedButton.icon( | ||||
|             onPressed: () { | ||||
|               onPressed(); | ||||
|             }, | ||||
|             icon: const Icon(Icons.close_rounded), | ||||
|             label: Text( | ||||
|               '$selectedItemCount', | ||||
|               style: const TextStyle( | ||||
|                 fontWeight: FontWeight.w600, | ||||
|                 fontSize: 18, | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|   | ||||
| @@ -30,6 +30,7 @@ class ImmichSliverAppBar extends ConsumerWidget { | ||||
|       floating: true, | ||||
|       pinned: false, | ||||
|       snap: false, | ||||
|       backgroundColor: Theme.of(context).appBarTheme.backgroundColor, | ||||
|       shape: const RoundedRectangleBorder( | ||||
|         borderRadius: BorderRadius.all(Radius.circular(5)), | ||||
|       ), | ||||
| @@ -57,7 +58,7 @@ class ImmichSliverAppBar extends ConsumerWidget { | ||||
|                   child: GestureDetector( | ||||
|                     onTap: () => Scaffold.of(context).openDrawer(), | ||||
|                     child: Material( | ||||
|                       color: Colors.grey[200], | ||||
|                       // color: Colors.grey[200], | ||||
|                       elevation: 1, | ||||
|                       shape: RoundedRectangleBorder( | ||||
|                         borderRadius: BorderRadius.circular(50.0), | ||||
| @@ -77,13 +78,12 @@ class ImmichSliverAppBar extends ConsumerWidget { | ||||
|           ); | ||||
|         }, | ||||
|       ), | ||||
|       title: Text( | ||||
|       title: const Text( | ||||
|         'IMMICH', | ||||
|         style: TextStyle( | ||||
|           fontFamily: 'SnowburstOne', | ||||
|           fontWeight: FontWeight.bold, | ||||
|           fontSize: 22, | ||||
|           color: Theme.of(context).primaryColor, | ||||
|         ), | ||||
|       ), | ||||
|       actions: [ | ||||
| @@ -112,12 +112,13 @@ class ImmichSliverAppBar extends ConsumerWidget { | ||||
|                   ? const Icon(Icons.backup_rounded) | ||||
|                   : Badge( | ||||
|                       padding: const EdgeInsets.all(4), | ||||
|                       elevation: 2, | ||||
|                       elevation: 3, | ||||
|                       position: BadgePosition.bottomEnd(bottom: -4, end: -4), | ||||
|                       badgeColor: Colors.white, | ||||
|                       badgeContent: const Icon( | ||||
|                         Icons.cloud_off_rounded, | ||||
|                         size: 8, | ||||
|                         color: Colors.indigo, | ||||
|                       ), | ||||
|                       child: const Icon(Icons.backup_rounded), | ||||
|                     ), | ||||
|   | ||||
| @@ -22,7 +22,7 @@ class MonthlyTitleText extends StatelessWidget { | ||||
|           style: TextStyle( | ||||
|             fontSize: 26, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: Theme.of(context).primaryColor, | ||||
|             color: Theme.of(context).textTheme.headline1?.color, | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|   | ||||
| @@ -22,17 +22,16 @@ class ProfileDrawer extends HookConsumerWidget { | ||||
|           height: double.infinity, | ||||
|           child: Icon( | ||||
|             Icons.logout_rounded, | ||||
|             color: Colors.grey[700], | ||||
|             color: Theme.of(context).textTheme.labelMedium?.color, | ||||
|             size: 20, | ||||
|           ), | ||||
|         ), | ||||
|         title: Text( | ||||
|           "profile_drawer_sign_out", | ||||
|           style: TextStyle( | ||||
|             color: Colors.grey[700], | ||||
|             fontSize: 12, | ||||
|             fontWeight: FontWeight.bold, | ||||
|           ), | ||||
|           style: Theme.of(context) | ||||
|               .textTheme | ||||
|               .labelLarge | ||||
|               ?.copyWith(fontWeight: FontWeight.bold), | ||||
|         ).tr(), | ||||
|         onTap: () async { | ||||
|           bool res = await ref.watch(authenticationProvider.notifier).logout(); | ||||
| @@ -54,17 +53,16 @@ class ProfileDrawer extends HookConsumerWidget { | ||||
|           height: double.infinity, | ||||
|           child: Icon( | ||||
|             Icons.settings_rounded, | ||||
|             color: Colors.grey[700], | ||||
|             color: Theme.of(context).textTheme.labelMedium?.color, | ||||
|             size: 20, | ||||
|           ), | ||||
|         ), | ||||
|         title: Text( | ||||
|           "profile_drawer_settings", | ||||
|           style: TextStyle( | ||||
|             color: Colors.grey[700], | ||||
|             fontSize: 12, | ||||
|             fontWeight: FontWeight.bold, | ||||
|           ), | ||||
|           style: Theme.of(context) | ||||
|               .textTheme | ||||
|               .labelLarge | ||||
|               ?.copyWith(fontWeight: FontWeight.bold), | ||||
|         ).tr(), | ||||
|         onTap: () { | ||||
|           AutoRouter.of(context).push(const SettingsRoute()); | ||||
|   | ||||
| @@ -23,6 +23,7 @@ class ProfileDrawerHeader extends HookConsumerWidget { | ||||
|     final uploadProfileImageStatus = | ||||
|         ref.watch(uploadProfileImageProvider).status; | ||||
|     var dummmy = Random().nextInt(1024); | ||||
|     final isDarkMode = Theme.of(context).brightness == Brightness.dark; | ||||
|  | ||||
|     _buildUserProfileImage() { | ||||
|       if (authState.profileImagePath.isEmpty) { | ||||
| @@ -104,13 +105,19 @@ class ProfileDrawerHeader extends HookConsumerWidget { | ||||
|     ); | ||||
|  | ||||
|     return DrawerHeader( | ||||
|       decoration: const BoxDecoration( | ||||
|       decoration: BoxDecoration( | ||||
|         gradient: LinearGradient( | ||||
|           colors: [ | ||||
|             Color.fromARGB(255, 216, 219, 238), | ||||
|             Color.fromARGB(255, 242, 242, 242), | ||||
|             Colors.white, | ||||
|           ], | ||||
|           colors: isDarkMode | ||||
|               ? [ | ||||
|                   const Color.fromARGB(255, 22, 25, 48), | ||||
|                   const Color.fromARGB(255, 13, 13, 13), | ||||
|                   const Color.fromARGB(255, 0, 0, 0), | ||||
|                 ] | ||||
|               : [ | ||||
|                   const Color.fromARGB(255, 216, 219, 238), | ||||
|                   const Color.fromARGB(255, 242, 242, 242), | ||||
|                   Colors.white, | ||||
|                 ], | ||||
|           begin: Alignment.centerRight, | ||||
|           end: Alignment.centerLeft, | ||||
|         ), | ||||
| @@ -129,8 +136,8 @@ class ProfileDrawerHeader extends HookConsumerWidget { | ||||
|                 child: GestureDetector( | ||||
|                   onTap: _pickUserProfileImage, | ||||
|                   child: Material( | ||||
|                     color: Colors.grey[50], | ||||
|                     elevation: 2, | ||||
|                     color: Colors.grey[100], | ||||
|                     elevation: 3, | ||||
|                     shape: RoundedRectangleBorder( | ||||
|                       borderRadius: BorderRadius.circular(50.0), | ||||
|                     ), | ||||
| @@ -157,7 +164,7 @@ class ProfileDrawerHeader extends HookConsumerWidget { | ||||
|           ), | ||||
|           Text( | ||||
|             authState.userEmail, | ||||
|             style: TextStyle(color: Colors.grey[800], fontSize: 12), | ||||
|             style: Theme.of(context).textTheme.labelMedium, | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class ServerInfoBox extends HookConsumerWidget { | ||||
|       padding: const EdgeInsets.all(8.0), | ||||
|       child: Card( | ||||
|         elevation: 0, | ||||
|         color: Colors.grey[100], | ||||
|         color: Theme.of(context).scaffoldBackgroundColor, | ||||
|         shape: RoundedRectangleBorder( | ||||
|           borderRadius: BorderRadius.circular(5), // if you need this | ||||
|           side: const BorderSide( | ||||
| @@ -65,7 +65,10 @@ class ServerInfoBox extends HookConsumerWidget { | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               const Divider(), | ||||
|               const Divider( | ||||
|                 color: Color.fromARGB(101, 201, 201, 201), | ||||
|                 thickness: 1, | ||||
|               ), | ||||
|               Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
| @@ -87,7 +90,10 @@ class ServerInfoBox extends HookConsumerWidget { | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|               const Divider(), | ||||
|               const Divider( | ||||
|                 color: Color.fromARGB(101, 201, 201, 201), | ||||
|                 thickness: 1, | ||||
|               ), | ||||
|               Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|   | ||||
| @@ -117,9 +117,9 @@ class HomePage extends HookConsumerWidget { | ||||
|               ], | ||||
|             ), | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.only(top: 50.0), | ||||
|               padding: const EdgeInsets.only(top: 60.0, bottom: 30.0), | ||||
|               child: DraggableScrollbar.semicircle( | ||||
|                 backgroundColor: Theme.of(context).primaryColor, | ||||
|                 backgroundColor: Theme.of(context).hintColor, | ||||
|                 controller: scrollController, | ||||
|                 heightScrollThumb: 48.0, | ||||
|                 child: CustomScrollView( | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ThumbnailWithInfo extends StatelessWidget { | ||||
|       child: Padding( | ||||
|         padding: const EdgeInsets.only(right: 8.0), | ||||
|         child: SizedBox( | ||||
|           width: MediaQuery.of(context).size.width / 2, | ||||
|           width: MediaQuery.of(context).size.width / 3, | ||||
|           child: Stack( | ||||
|             alignment: Alignment.bottomCenter, | ||||
|             children: [ | ||||
| @@ -58,7 +58,7 @@ class ThumbnailWithInfo extends StatelessWidget { | ||||
|                     style: const TextStyle( | ||||
|                       color: Colors.white, | ||||
|                       fontWeight: FontWeight.bold, | ||||
|                       fontSize: 16, | ||||
|                       fontSize: 14, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|   | ||||
| @@ -29,6 +29,8 @@ class SearchPage extends HookConsumerWidget { | ||||
|     AsyncValue<List<CuratedObjectsResponseDto>> curatedObjects = | ||||
|         ref.watch(getCuratedObjectProvider); | ||||
|  | ||||
|     double imageSize = MediaQuery.of(context).size.width / 3; | ||||
|  | ||||
|     useEffect( | ||||
|       () { | ||||
|         searchFocusNode = FocusNode(); | ||||
| @@ -46,15 +48,15 @@ class SearchPage extends HookConsumerWidget { | ||||
|  | ||||
|     _buildPlaces() { | ||||
|       return curatedLocation.when( | ||||
|         loading: () => const SizedBox( | ||||
|           height: 200, | ||||
|           child: Center(child: ImmichLoadingIndicator()), | ||||
|         loading: () => SizedBox( | ||||
|           height: imageSize, | ||||
|           child: const Center(child: ImmichLoadingIndicator()), | ||||
|         ), | ||||
|         error: (err, stack) => Text('Error: $err'), | ||||
|         data: (curatedLocations) { | ||||
|           return curatedLocations.isNotEmpty | ||||
|               ? SizedBox( | ||||
|                   height: MediaQuery.of(context).size.width / 2, | ||||
|                   height: imageSize, | ||||
|                   child: ListView.builder( | ||||
|                     padding: const EdgeInsets.only(left: 16), | ||||
|                     scrollDirection: Axis.horizontal, | ||||
| @@ -76,7 +78,7 @@ class SearchPage extends HookConsumerWidget { | ||||
|                   ), | ||||
|                 ) | ||||
|               : SizedBox( | ||||
|                   height: MediaQuery.of(context).size.width / 2, | ||||
|                   height: imageSize, | ||||
|                   child: ListView.builder( | ||||
|                     padding: const EdgeInsets.only(left: 16), | ||||
|                     scrollDirection: Axis.horizontal, | ||||
| @@ -105,7 +107,7 @@ class SearchPage extends HookConsumerWidget { | ||||
|         data: (objects) { | ||||
|           return objects.isNotEmpty | ||||
|               ? SizedBox( | ||||
|                   height: MediaQuery.of(context).size.width / 2, | ||||
|                   height: imageSize, | ||||
|                   child: ListView.builder( | ||||
|                     padding: const EdgeInsets.only(left: 16), | ||||
|                     scrollDirection: Axis.horizontal, | ||||
| @@ -113,7 +115,7 @@ class SearchPage extends HookConsumerWidget { | ||||
|                     itemBuilder: ((context, index) { | ||||
|                       var curatedObjectInfo = objects[index]; | ||||
|                       var thumbnailRequestUrl = | ||||
|                           '${box.get(serverEndpointKey)}/asset/file?aid=${curatedObjectInfo.deviceAssetId}&did=${curatedObjectInfo.deviceId}&isThumb=true'; | ||||
|                           '${box.get(serverEndpointKey)}/asset/thumbnail/${curatedObjectInfo.id}'; | ||||
|  | ||||
|                       return ThumbnailWithInfo( | ||||
|                         imageUrl: thumbnailRequestUrl, | ||||
| @@ -131,7 +133,8 @@ class SearchPage extends HookConsumerWidget { | ||||
|                   ), | ||||
|                 ) | ||||
|               : SizedBox( | ||||
|                   height: MediaQuery.of(context).size.width / 2, | ||||
|                   // height: imageSize, | ||||
|                   width: imageSize, | ||||
|                   child: ListView.builder( | ||||
|                     padding: const EdgeInsets.only(left: 16), | ||||
|                     scrollDirection: Axis.horizontal, | ||||
| @@ -163,12 +166,13 @@ class SearchPage extends HookConsumerWidget { | ||||
|         child: Stack( | ||||
|           children: [ | ||||
|             ListView( | ||||
|               shrinkWrap: true, | ||||
|               children: [ | ||||
|                 Padding( | ||||
|                   padding: const EdgeInsets.all(16.0), | ||||
|                   child: const Text( | ||||
|                     "search_page_places", | ||||
|                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), | ||||
|                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), | ||||
|                   ).tr(), | ||||
|                 ), | ||||
|                 _buildPlaces(), | ||||
| @@ -176,7 +180,7 @@ class SearchPage extends HookConsumerWidget { | ||||
|                   padding: const EdgeInsets.all(16.0), | ||||
|                   child: const Text( | ||||
|                     "search_page_things", | ||||
|                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), | ||||
|                     style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), | ||||
|                   ).tr(), | ||||
|                 ), | ||||
|                 _buildThings() | ||||
|   | ||||
| @@ -172,7 +172,7 @@ class SearchResultPage extends HookConsumerWidget { | ||||
|           }); | ||||
|  | ||||
|           return DraggableScrollbar.semicircle( | ||||
|             backgroundColor: Theme.of(context).primaryColor, | ||||
|             backgroundColor: Theme.of(context).hintColor, | ||||
|             controller: scrollController, | ||||
|             heightScrollThumb: 48.0, | ||||
|             child: CustomScrollView( | ||||
|   | ||||
| @@ -0,0 +1,4 @@ | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
|  | ||||
| final appSettingsServiceProvider = Provider((ref) => AppSettingsService()); | ||||
| @@ -5,7 +5,7 @@ import 'package:immich_mobile/constants/hive_box.dart'; | ||||
| 
 | ||||
| enum AppSettingsEnum { | ||||
|   threeStageLoading, // true, false, | ||||
|   themeMode, // "light","dark" | ||||
|   themeMode, // "light","dark","system" | ||||
| } | ||||
| 
 | ||||
| class AppSettingsService { | ||||
| @@ -61,8 +61,8 @@ class AppSettingsService { | ||||
| 
 | ||||
|     // Default value of themeMode is "light" | ||||
|     if (settingType == AppSettingsEnum.themeMode) { | ||||
|       hiveBox.put(settingKey, "light"); | ||||
|       return "light"; | ||||
|       hiveBox.put(settingKey, "system"); | ||||
|       return "system"; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @@ -75,5 +75,3 @@ class AppSettingsService { | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| final appSettingsServiceProvider = Provider((ref) => AppSettingsService()); | ||||
| @@ -1,3 +1,4 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/three_stage_loading.dart'; | ||||
|  | ||||
| @@ -8,20 +9,21 @@ class ImageViewerQualitySetting extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return const ExpansionTile( | ||||
|       title: Text( | ||||
|         'Image viewer quality', | ||||
|     return ExpansionTile( | ||||
|       textColor: Theme.of(context).primaryColor, | ||||
|       title: const Text( | ||||
|         'theme_setting_image_viewer_quality_title', | ||||
|         style: TextStyle( | ||||
|           fontWeight: FontWeight.bold, | ||||
|         ), | ||||
|       ), | ||||
|       subtitle: Text( | ||||
|         'Adjust the quality of the detail image viewer', | ||||
|       ).tr(), | ||||
|       subtitle: const Text( | ||||
|         'theme_setting_image_viewer_quality_subtitle', | ||||
|         style: TextStyle( | ||||
|           fontSize: 13, | ||||
|         ), | ||||
|       ), | ||||
|       children: [ | ||||
|       ).tr(), | ||||
|       children: const [ | ||||
|         ThreeStageLoading(), | ||||
|       ], | ||||
|     ); | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| 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/shared/services/app_settings.service.dart'; | ||||
| import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
|  | ||||
| class ThreeStageLoading extends HookConsumerWidget { | ||||
|   const ThreeStageLoading({ | ||||
| @@ -35,18 +37,18 @@ class ThreeStageLoading extends HookConsumerWidget { | ||||
|  | ||||
|     return SwitchListTile.adaptive( | ||||
|       title: const Text( | ||||
|         "Enable three stage loading", | ||||
|         "theme_setting_three_stage_loading_title", | ||||
|         style: TextStyle( | ||||
|           fontSize: 12, | ||||
|           fontWeight: FontWeight.bold, | ||||
|         ), | ||||
|       ), | ||||
|       ).tr(), | ||||
|       subtitle: const Text( | ||||
|         "The three-stage loading delivers the best quality image in exchange for a slower loading speed", | ||||
|         "theme_setting_three_stage_loading_subtitle", | ||||
|         style: TextStyle( | ||||
|           fontSize: 12, | ||||
|         ), | ||||
|       ), | ||||
|       ).tr(), | ||||
|       value: isEnable.value, | ||||
|       onChanged: onSwitchChanged, | ||||
|     ); | ||||
|   | ||||
							
								
								
									
										107
									
								
								mobile/lib/modules/settings/ui/theme_setting/theme_setting.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								mobile/lib/modules/settings/ui/theme_setting/theme_setting.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| 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/modules/settings/providers/app_settings.provider.dart'; | ||||
| import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/three_stage_loading.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
| import 'package:immich_mobile/utils/immich_app_theme.dart'; | ||||
|  | ||||
| class ThemeSetting extends HookConsumerWidget { | ||||
|   const ThemeSetting({ | ||||
|     Key? key, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final currentTheme = useState<ThemeMode>(ThemeMode.system); | ||||
|  | ||||
|     useEffect( | ||||
|       () { | ||||
|         currentTheme.value = ref.read(immichThemeProvider); | ||||
|         return null; | ||||
|       }, | ||||
|       [], | ||||
|     ); | ||||
|  | ||||
|     return ExpansionTile( | ||||
|       textColor: Theme.of(context).primaryColor, | ||||
|       title: const Text( | ||||
|         'theme_setting_theme_title', | ||||
|         style: TextStyle( | ||||
|           fontWeight: FontWeight.bold, | ||||
|         ), | ||||
|       ).tr(), | ||||
|       subtitle: const Text( | ||||
|         'theme_setting_theme_subtitle', | ||||
|         style: TextStyle( | ||||
|           fontSize: 13, | ||||
|         ), | ||||
|       ).tr(), | ||||
|       children: [ | ||||
|         SwitchListTile.adaptive( | ||||
|           activeColor: Theme.of(context).primaryColor, | ||||
|           title: const Text( | ||||
|             'theme_setting_system_theme_switch', | ||||
|             style: TextStyle( | ||||
|               fontSize: 12.0, | ||||
|               fontWeight: FontWeight.bold, | ||||
|             ), | ||||
|           ).tr(), | ||||
|           value: currentTheme.value == ThemeMode.system, | ||||
|           onChanged: (bool isSystem) { | ||||
|             var currentSystemBrightness = | ||||
|                 MediaQuery.of(context).platformBrightness; | ||||
|  | ||||
|             if (isSystem) { | ||||
|               currentTheme.value = ThemeMode.system; | ||||
|               ref.watch(immichThemeProvider.notifier).state = ThemeMode.system; | ||||
|               ref | ||||
|                   .watch(appSettingsServiceProvider) | ||||
|                   .setSetting(AppSettingsEnum.themeMode, "system"); | ||||
|             } else { | ||||
|               if (currentSystemBrightness == Brightness.light) { | ||||
|                 currentTheme.value = ThemeMode.light; | ||||
|                 ref.watch(immichThemeProvider.notifier).state = ThemeMode.light; | ||||
|                 ref | ||||
|                     .watch(appSettingsServiceProvider) | ||||
|                     .setSetting(AppSettingsEnum.themeMode, "light"); | ||||
|               } else if (currentSystemBrightness == Brightness.dark) { | ||||
|                 currentTheme.value = ThemeMode.dark; | ||||
|                 ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark; | ||||
|                 ref | ||||
|                     .watch(appSettingsServiceProvider) | ||||
|                     .setSetting(AppSettingsEnum.themeMode, "dark"); | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|         ), | ||||
|         if (currentTheme.value != ThemeMode.system) | ||||
|           SwitchListTile.adaptive( | ||||
|             activeColor: Theme.of(context).primaryColor, | ||||
|             title: const Text( | ||||
|               'theme_setting_dark_mode_switch', | ||||
|               style: TextStyle( | ||||
|                 fontSize: 12.0, | ||||
|                 fontWeight: FontWeight.bold, | ||||
|               ), | ||||
|             ).tr(), | ||||
|             value: ref.watch(immichThemeProvider) == ThemeMode.dark, | ||||
|             onChanged: (bool isDark) { | ||||
|               if (isDark) { | ||||
|                 ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark; | ||||
|                 ref | ||||
|                     .watch(appSettingsServiceProvider) | ||||
|                     .setSetting(AppSettingsEnum.themeMode, "dark"); | ||||
|               } else { | ||||
|                 ref.watch(immichThemeProvider.notifier).state = ThemeMode.light; | ||||
|                 ref | ||||
|                     .watch(appSettingsServiceProvider) | ||||
|                     .setSetting(AppSettingsEnum.themeMode, "light"); | ||||
|               } | ||||
|             }, | ||||
|           ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,6 +1,8 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/image_viewer_quality_setting.dart'; | ||||
| import 'package:immich_mobile/modules/settings/ui/theme_setting/theme_setting.dart'; | ||||
|  | ||||
| class SettingsPage extends HookConsumerWidget { | ||||
|   const SettingsPage({Key? key}) : super(key: key); | ||||
| @@ -20,12 +22,12 @@ class SettingsPage extends HookConsumerWidget { | ||||
|         automaticallyImplyLeading: false, | ||||
|         centerTitle: false, | ||||
|         title: const Text( | ||||
|           'Settings', | ||||
|           'setting_pages_app_bar_settings', | ||||
|           style: TextStyle( | ||||
|             fontSize: 16, | ||||
|             fontWeight: FontWeight.bold, | ||||
|           ), | ||||
|         ), | ||||
|         ).tr(), | ||||
|       ), | ||||
|       body: ListView( | ||||
|         children: [ | ||||
| @@ -33,10 +35,7 @@ class SettingsPage extends HookConsumerWidget { | ||||
|             context: context, | ||||
|             tiles: [ | ||||
|               const ImageViewerQualitySetting(), | ||||
|               const SettingListTile( | ||||
|                 title: 'Theme', | ||||
|                 subtitle: 'Choose between light and dark theme', | ||||
|               ), | ||||
|               const ThemeSetting(), | ||||
|             ], | ||||
|           ).toList(), | ||||
|         ], | ||||
| @@ -44,38 +43,3 @@ class SettingsPage extends HookConsumerWidget { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class SettingListTile extends StatelessWidget { | ||||
|   const SettingListTile({ | ||||
|     required this.title, | ||||
|     required this.subtitle, | ||||
|     Key? key, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   final String title; | ||||
|   final String subtitle; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListTile( | ||||
|       dense: true, | ||||
|       title: Text( | ||||
|         title, | ||||
|         style: const TextStyle( | ||||
|           fontWeight: FontWeight.bold, | ||||
|         ), | ||||
|       ), | ||||
|       subtitle: Text( | ||||
|         subtitle, | ||||
|         style: const TextStyle( | ||||
|           fontSize: 12, | ||||
|         ), | ||||
|       ), | ||||
|       trailing: const Icon( | ||||
|         Icons.keyboard_arrow_right_rounded, | ||||
|         size: 24, | ||||
|       ), | ||||
|       onTap: () {}, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/hive_box.dart'; | ||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | ||||
| import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; | ||||
| import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart'; | ||||
| import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | ||||
| @@ -49,7 +48,6 @@ class SplashScreenPage extends HookConsumerWidget { | ||||
|     ); | ||||
|  | ||||
|     return Scaffold( | ||||
|       backgroundColor: immichBackgroundColor, | ||||
|       body: Center( | ||||
|         child: Column( | ||||
|           mainAxisAlignment: MainAxisAlignment.center, | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | ||||
| import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart'; | ||||
| import 'package:immich_mobile/routing/router.dart'; | ||||
|  | ||||
| @@ -36,8 +35,6 @@ class TabControllerPage extends ConsumerWidget { | ||||
|             bottomNavigationBar: isMultiSelectEnable | ||||
|                 ? null | ||||
|                 : BottomNavigationBar( | ||||
|                     type: BottomNavigationBarType.fixed, | ||||
|                     backgroundColor: immichBackgroundColor, | ||||
|                     selectedLabelStyle: const TextStyle( | ||||
|                       fontSize: 13, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|   | ||||
							
								
								
									
										133
									
								
								mobile/lib/utils/immich_app_theme.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								mobile/lib/utils/immich_app_theme.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/immich_colors.dart'; | ||||
| import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; | ||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||
|  | ||||
| final immichThemeProvider = StateProvider<ThemeMode>((ref) { | ||||
|   var themeMode = ref | ||||
|       .watch(appSettingsServiceProvider) | ||||
|       .getSetting(AppSettingsEnum.themeMode); | ||||
|  | ||||
|   debugPrint("Current themeMode $themeMode"); | ||||
|  | ||||
|   if (themeMode == "light") { | ||||
|     return ThemeMode.light; | ||||
|   } else if (themeMode == "dark") { | ||||
|     return ThemeMode.dark; | ||||
|   } else { | ||||
|     return ThemeMode.system; | ||||
|   } | ||||
| }); | ||||
|  | ||||
| ThemeData immichDarkTheme = ThemeData( | ||||
|   useMaterial3: true, | ||||
|   brightness: Brightness.dark, | ||||
|   primarySwatch: Colors.indigo, | ||||
|   primaryColor: immichDarkThemePrimaryColor, | ||||
|   scaffoldBackgroundColor: immichDarkBackgroundColor, | ||||
|   hintColor: Colors.grey[600], | ||||
|   fontFamily: 'WorkSans', | ||||
|   snackBarTheme: const SnackBarThemeData( | ||||
|     contentTextStyle: TextStyle(fontFamily: 'WorkSans'), | ||||
|   ), | ||||
|   appBarTheme: AppBarTheme( | ||||
|     titleTextStyle: TextStyle( | ||||
|       fontFamily: 'WorkSans', | ||||
|       color: immichDarkThemePrimaryColor, | ||||
|     ), | ||||
|     backgroundColor: const Color.fromARGB(255, 32, 33, 35), | ||||
|     foregroundColor: immichDarkThemePrimaryColor, | ||||
|     elevation: 1, | ||||
|     centerTitle: true, | ||||
|     systemOverlayStyle: SystemUiOverlayStyle.light, | ||||
|   ), | ||||
|   bottomNavigationBarTheme: BottomNavigationBarThemeData( | ||||
|     type: BottomNavigationBarType.fixed, | ||||
|     backgroundColor: const Color.fromARGB(255, 35, 36, 37), | ||||
|     selectedItemColor: immichDarkThemePrimaryColor, | ||||
|   ), | ||||
|   drawerTheme: DrawerThemeData( | ||||
|     backgroundColor: immichDarkBackgroundColor, | ||||
|     scrimColor: Colors.white.withOpacity(0.1), | ||||
|   ), | ||||
|   textTheme: TextTheme( | ||||
|     headline1: const TextStyle( | ||||
|       fontSize: 26, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: Color.fromARGB(255, 255, 255, 255), | ||||
|     ), | ||||
|     headline2: const TextStyle( | ||||
|       fontSize: 14, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: Color.fromARGB(255, 148, 151, 155), | ||||
|     ), | ||||
|     headline3: TextStyle( | ||||
|       fontSize: 12, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: immichDarkThemePrimaryColor, | ||||
|     ), | ||||
|   ), | ||||
|   cardColor: Colors.grey[900], | ||||
|   elevatedButtonTheme: ElevatedButtonThemeData( | ||||
|     style: ElevatedButton.styleFrom( | ||||
|       onPrimary: Colors.black87, | ||||
|       primary: immichDarkThemePrimaryColor, | ||||
|     ), | ||||
|   ), | ||||
| ); | ||||
|  | ||||
| ThemeData immichLightTheme = ThemeData( | ||||
|   useMaterial3: true, | ||||
|   brightness: Brightness.light, | ||||
|   primarySwatch: Colors.indigo, | ||||
|   hintColor: Colors.indigo, | ||||
|   fontFamily: 'WorkSans', | ||||
|   scaffoldBackgroundColor: immichBackgroundColor, | ||||
|   snackBarTheme: const SnackBarThemeData( | ||||
|     contentTextStyle: TextStyle(fontFamily: 'WorkSans'), | ||||
|   ), | ||||
|   appBarTheme: AppBarTheme( | ||||
|     titleTextStyle: const TextStyle( | ||||
|       fontFamily: 'WorkSans', | ||||
|       color: Colors.indigo, | ||||
|     ), | ||||
|     backgroundColor: immichBackgroundColor, | ||||
|     foregroundColor: Colors.indigo, | ||||
|     elevation: 1, | ||||
|     centerTitle: true, | ||||
|     systemOverlayStyle: SystemUiOverlayStyle.dark, | ||||
|   ), | ||||
|   bottomNavigationBarTheme: BottomNavigationBarThemeData( | ||||
|     type: BottomNavigationBarType.fixed, | ||||
|     backgroundColor: immichBackgroundColor, | ||||
|     selectedItemColor: Colors.indigo, | ||||
|   ), | ||||
|   drawerTheme: DrawerThemeData( | ||||
|     backgroundColor: immichBackgroundColor, | ||||
|   ), | ||||
|   textTheme: const TextTheme( | ||||
|     headline1: TextStyle( | ||||
|       fontSize: 26, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: Colors.indigo, | ||||
|     ), | ||||
|     headline2: TextStyle( | ||||
|       fontSize: 14, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: Colors.black87, | ||||
|     ), | ||||
|     headline3: TextStyle( | ||||
|       fontSize: 12, | ||||
|       fontWeight: FontWeight.bold, | ||||
|       color: Colors.indigo, | ||||
|     ), | ||||
|   ), | ||||
|   elevatedButtonTheme: ElevatedButtonThemeData( | ||||
|     style: ElevatedButton.styleFrom( | ||||
|       primary: Colors.indigo, | ||||
|       onPrimary: Colors.white, | ||||
|     ), | ||||
|   ), | ||||
| ); | ||||
| @@ -2,7 +2,7 @@ name: immich_mobile | ||||
| description: Immich - selfhosted backup media file on mobile phone | ||||
|  | ||||
| publish_to: "none" | ||||
| version: 1.22.0+32 | ||||
| version: 1.23.0+33 | ||||
|  | ||||
| environment: | ||||
|   sdk: ">=2.17.0 <3.0.0" | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export interface IServerVersion { | ||||
|  | ||||
| export const serverVersion: IServerVersion = { | ||||
|   major: 1, | ||||
|   minor: 22, | ||||
|   minor: 23, | ||||
|   patch: 0, | ||||
|   build: 0, | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user