refactor(mobile): immich loading overlay (#5320)

* refactor: dcm fixes

* refactor: ImmichLoadingOverlay to custom hook

* chore: dart fixes

* pr changes

* fix: process overlay add / remove in postframecallback

---------

Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
shenlong 2023-11-29 04:20:00 +00:00 committed by GitHub
parent 513f252a0c
commit 527d602a9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 146 additions and 128 deletions

View File

@ -49,7 +49,6 @@ dart_code_metrics:
# Common # Common
- avoid-accessing-collections-by-constant-index - avoid-accessing-collections-by-constant-index
- avoid-accessing-other-classes-private-members - avoid-accessing-other-classes-private-members
- avoid-async-call-in-sync-function
- avoid-cascade-after-if-null - avoid-cascade-after-if-null
- avoid-collapsible-if - avoid-collapsible-if
- avoid-collection-methods-with-unrelated-types - avoid-collection-methods-with-unrelated-types

View File

@ -45,7 +45,7 @@ class ImmichTestHelper {
await tester.pumpWidget( await tester.pumpWidget(
ProviderScope( ProviderScope(
overrides: [dbProvider.overrideWithValue(db)], overrides: [dbProvider.overrideWithValue(db)],
child: app.getMainWidget(), child: const app.MainWidget(),
), ),
); );
// Post run tasks // Post run tasks

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Color immichBackgroundColor = const Color(0xFFf6f8fe); const Color immichBackgroundColor = Color(0xFFf6f8fe);
Color immichDarkBackgroundColor = const Color.fromARGB(255, 0, 0, 0); const Color immichDarkBackgroundColor = Color.fromARGB(255, 0, 0, 0);
Color immichDarkThemePrimaryColor = const Color.fromARGB(255, 173, 203, 250); const Color immichDarkThemePrimaryColor = Color.fromARGB(255, 173, 203, 250);

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
@ -7,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:timezone/data/latest.dart'; import 'package:timezone/data/latest.dart';
import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/constants/locales.dart';
import 'package:immich_mobile/modules/backup/background_service/background.service.dart'; import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
@ -28,7 +30,6 @@ import 'package:immich_mobile/shared/providers/app_state.provider.dart';
import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:immich_mobile/shared/services/immich_logger.service.dart'; import 'package:immich_mobile/shared/services/immich_logger.service.dart';
import 'package:immich_mobile/shared/services/local_notification.service.dart'; import 'package:immich_mobile/shared/services/local_notification.service.dart';
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
import 'package:immich_mobile/utils/immich_app_theme.dart'; import 'package:immich_mobile/utils/immich_app_theme.dart';
import 'package:immich_mobile/utils/migration.dart'; import 'package:immich_mobile/utils/migration.dart';
@ -43,10 +44,11 @@ void main() async {
await initApp(); await initApp();
await migrateDatabaseIfNeeded(db); await migrateDatabaseIfNeeded(db);
HttpOverrides.global = HttpSSLCertOverride(); HttpOverrides.global = HttpSSLCertOverride();
runApp( runApp(
ProviderScope( ProviderScope(
overrides: [dbProvider.overrideWithValue(db)], overrides: [dbProvider.overrideWithValue(db)],
child: getMainWidget(), child: const MainWidget(),
), ),
); );
} }
@ -108,16 +110,6 @@ Future<Isar> loadDb() async {
return db; return db;
} }
Widget getMainWidget() {
return EasyLocalization(
supportedLocales: locales,
path: translationsPath,
useFallbackTranslations: true,
fallbackLocale: locales.first,
child: const ImmichApp(),
);
}
class ImmichApp extends ConsumerStatefulWidget { class ImmichApp extends ConsumerStatefulWidget {
const ImmichApp({super.key}); const ImmichApp({super.key});
@ -167,10 +159,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
// Android 8 does not support transparent app bars // Android 8 does not support transparent app bars
final info = await DeviceInfoPlugin().androidInfo; final info = await DeviceInfoPlugin().androidInfo;
if (info.version.sdkInt <= 26) { if (info.version.sdkInt <= 26) {
overlayStyle = overlayStyle = context.isDarkTheme
MediaQuery.of(context).platformBrightness == Brightness.light ? SystemUiOverlayStyle.dark
? SystemUiOverlayStyle.light : SystemUiOverlayStyle.light;
: SystemUiOverlayStyle.dark;
} }
} }
SystemChrome.setSystemUIOverlayStyle(overlayStyle); SystemChrome.setSystemUIOverlayStyle(overlayStyle);
@ -202,22 +193,33 @@ class ImmichAppState extends ConsumerState<ImmichApp>
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
home: Stack( home: MaterialApp.router(
children: [ title: 'Immich',
MaterialApp.router( debugShowCheckedModeBanner: false,
title: 'Immich', themeMode: ref.watch(immichThemeProvider),
debugShowCheckedModeBanner: false, darkTheme: immichDarkTheme,
themeMode: ref.watch(immichThemeProvider), theme: immichLightTheme,
darkTheme: immichDarkTheme, routeInformationParser: router.defaultRouteParser(),
theme: immichLightTheme, routerDelegate: router.delegate(
routeInformationParser: router.defaultRouteParser(), navigatorObservers: () => [TabNavigationObserver(ref: ref)],
routerDelegate: router.delegate( ),
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
),
),
const ImmichLoadingOverlay(),
],
), ),
); );
} }
} }
// ignore: prefer-single-widget-per-file
class MainWidget extends StatelessWidget {
const MainWidget({super.key});
@override
Widget build(BuildContext context) {
return EasyLocalization(
supportedLocales: locales,
path: translationsPath,
useFallbackTranslations: true,
fallbackLocale: locales.first,
child: const ImmichApp(),
);
}
}

View File

@ -43,6 +43,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText; final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum; final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
final isProcessing = useProcessingOverlay();
final comments = album.shared final comments = album.shared
? ref.watch( ? ref.watch(
activityStatisticsStateProvider( activityStatisticsStateProvider(
@ -52,7 +53,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
: 0; : 0;
deleteAlbum() async { deleteAlbum() async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
final bool success; final bool success;
if (album.shared) { if (album.shared) {
@ -74,7 +75,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
); );
} }
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
Future<void> showConfirmationDialog() async { Future<void> showConfirmationDialog() async {
@ -122,7 +123,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
} }
void onLeaveAlbumPressed() async { void onLeaveAlbumPressed() async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
bool isSuccess = bool isSuccess =
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album); await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
@ -140,11 +141,11 @@ class AlbumViewerAppbar extends HookConsumerWidget
); );
} }
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
void onRemoveFromAlbumPressed() async { void onRemoveFromAlbumPressed() async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
bool isSuccess = bool isSuccess =
await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum( await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum(
@ -167,7 +168,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
); );
} }
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
void handleShareAssets( void handleShareAssets(
@ -198,9 +199,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
} }
void onShareAssetsTo() async { void onShareAssetsTo() async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
handleShareAssets(ref, context, selected); handleShareAssets(ref, context, selected);
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
buildBottomSheetActions() { buildBottomSheetActions() {

View File

@ -24,6 +24,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
final owner = album.owner.value; final owner = album.owner.value;
final userId = ref.watch(authenticationProvider).userId; final userId = ref.watch(authenticationProvider).userId;
final activityEnabled = useState(album.activityEnabled); final activityEnabled = useState(album.activityEnabled);
final isProcessing = useProcessingOverlay();
final isOwner = owner?.id == userId; final isOwner = owner?.id == userId;
void showErrorMessage() { void showErrorMessage() {
@ -37,7 +38,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
} }
void leaveAlbum() async { void leaveAlbum() async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
try { try {
final isSuccess = final isSuccess =
@ -54,11 +55,11 @@ class AlbumOptionsPage extends HookConsumerWidget {
showErrorMessage(); showErrorMessage();
} }
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
void removeUserFromAlbum(User user) async { void removeUserFromAlbum(User user) async {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
try { try {
await ref await ref
@ -71,7 +72,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
} }
context.pop(); context.pop();
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
void handleUserClick(User user) { void handleUserClick(User user) {

View File

@ -33,6 +33,7 @@ class AlbumViewerPage extends HookConsumerWidget {
final userId = ref.watch(authenticationProvider).userId; final userId = ref.watch(authenticationProvider).userId;
final selection = useState<Set<Asset>>({}); final selection = useState<Set<Asset>>({});
final multiSelectEnabled = useState(false); final multiSelectEnabled = useState(false);
final isProcessing = useProcessingOverlay();
useEffect( useEffect(
() { () {
@ -75,24 +76,21 @@ class AlbumViewerPage extends HookConsumerWidget {
), ),
); );
if (returnPayload != null) { if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) {
// Check if there is new assets add // Check if there is new assets add
if (returnPayload.selectedAssets.isNotEmpty) { isProcessing.value = true;
ImmichLoadingOverlayController.appLoader.show();
var addAssetsResult = var addAssetsResult =
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum( await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
returnPayload.selectedAssets, returnPayload.selectedAssets,
albumInfo, albumInfo,
); );
if (addAssetsResult != null && if (addAssetsResult != null && addAssetsResult.successfullyAdded > 0) {
addAssetsResult.successfullyAdded > 0) { ref.invalidate(albumDetailProvider(albumId));
ref.invalidate(albumDetailProvider(albumId));
}
ImmichLoadingOverlayController.appLoader.hide();
} }
isProcessing.value = false;
} }
} }
@ -102,7 +100,7 @@ class AlbumViewerPage extends HookConsumerWidget {
); );
if (sharedUserIds != null) { if (sharedUserIds != null) {
ImmichLoadingOverlayController.appLoader.show(); isProcessing.value = true;
var isSuccess = await ref var isSuccess = await ref
.watch(albumServiceProvider) .watch(albumServiceProvider)
@ -112,7 +110,7 @@ class AlbumViewerPage extends HookConsumerWidget {
ref.invalidate(albumDetailProvider(album.id)); ref.invalidate(albumDetailProvider(album.id));
} }
ImmichLoadingOverlayController.appLoader.hide(); isProcessing.value = false;
} }
} }

View File

@ -28,6 +28,7 @@ import 'package:immich_mobile/shared/providers/websocket.provider.dart';
import 'package:immich_mobile/shared/ui/immich_app_bar.dart'; import 'package:immich_mobile/shared/ui/immich_app_bar.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart';
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
import 'package:immich_mobile/utils/selection_handlers.dart'; import 'package:immich_mobile/utils/selection_handlers.dart';
class HomePage extends HookConsumerWidget { class HomePage extends HookConsumerWidget {
@ -50,7 +51,7 @@ class HomePage extends HookConsumerWidget {
final tipOneOpacity = useState(0.0); final tipOneOpacity = useState(0.0);
final refreshCount = useState(0); final refreshCount = useState(0);
final processing = useState(false); final processing = useProcessingOverlay();
useEffect( useEffect(
() { () {
@ -212,10 +213,10 @@ class HomePage extends HookConsumerWidget {
processing.value = true; processing.value = true;
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
try { try {
ref.read(manualUploadProvider.notifier).uploadAssets( ref.read(manualUploadProvider.notifier).uploadAssets(
context, context,
selection.value.where((a) => a.storage == AssetState.local), selection.value.where((a) => a.storage == AssetState.local),
); );
} finally { } finally {
processing.value = false; processing.value = false;
} }
@ -323,16 +324,12 @@ class HomePage extends HookConsumerWidget {
} else { } else {
refreshCount.value++; refreshCount.value++;
// set counter back to 0 if user does not request refresh again // set counter back to 0 if user does not request refresh again
Timer(const Duration(seconds: 4), () { Timer(const Duration(seconds: 4), () => refreshCount.value = 0);
refreshCount.value = 0;
});
} }
} }
buildLoadingIndicator() { buildLoadingIndicator() {
Timer(const Duration(seconds: 2), () { Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1);
tipOneOpacity.value = 1;
});
return Center( return Center(
child: Column( child: Column(
@ -415,7 +412,6 @@ class HomePage extends HookConsumerWidget {
selectionAssetState: selectionAssetState.value, selectionAssetState: selectionAssetState.value,
onStack: onStack, onStack: onStack,
), ),
if (processing.value) const Center(child: ImmichLoadingIndicator()),
], ],
), ),
); );

View File

@ -12,8 +12,8 @@ import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/asset.provider.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/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/confirm_dialog.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'; import 'package:immich_mobile/shared/ui/immich_toast.dart';
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
class TrashPage extends HookConsumerWidget { class TrashPage extends HookConsumerWidget {
const TrashPage({super.key}); const TrashPage({super.key});
@ -25,7 +25,7 @@ class TrashPage extends HookConsumerWidget {
ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
final selectionEnabledHook = useState(false); final selectionEnabledHook = useState(false);
final selection = useState(<Asset>{}); final selection = useState(<Asset>{});
final processing = useState(false); final processing = useProcessingOverlay();
void selectionListener( void selectionListener(
bool multiselect, bool multiselect,
@ -261,8 +261,6 @@ class TrashPage extends HookConsumerWidget {
), ),
), ),
if (selectionEnabledHook.value) buildBottomBar(), if (selectionEnabledHook.value) buildBottomBar(),
if (processing.value)
const Center(child: ImmichLoadingIndicator()),
], ],
), ),
), ),

View File

@ -21,7 +21,7 @@ class ImmichLoadingIndicator extends StatelessWidget {
padding: const EdgeInsets.all(15), padding: const EdgeInsets.all(15),
child: const CircularProgressIndicator( child: const CircularProgressIndicator(
color: Colors.white, color: Colors.white,
strokeWidth: 2, strokeWidth: 3,
), ),
); );
} }

View File

@ -1,41 +1,64 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
class ImmichLoadingOverlay extends StatelessWidget { final _loadingEntry = OverlayEntry(
const ImmichLoadingOverlay({ builder: (context) => SizedBox.square(
Key? key, dimension: double.infinity,
}) : super(key: key); child: DecoratedBox(
decoration:
BoxDecoration(color: context.colorScheme.surface.withAlpha(200)),
child: const Center(child: ImmichLoadingIndicator()),
),
),
);
ValueNotifier<bool> useProcessingOverlay() {
return use(const _LoadingOverlay());
}
class _LoadingOverlay extends Hook<ValueNotifier<bool>> {
const _LoadingOverlay();
@override @override
Widget build(BuildContext context) { _LoadingOverlayState createState() => _LoadingOverlayState();
return ValueListenableBuilder<bool>(
valueListenable:
ImmichLoadingOverlayController.appLoader.loaderShowingNotifier,
builder: (context, shouldShow, child) {
return shouldShow
? const Scaffold(
backgroundColor: Colors.black54,
body: Center(
child: ImmichLoadingIndicator(),
),
)
: const SizedBox();
},
);
}
} }
class ImmichLoadingOverlayController { class _LoadingOverlayState
static final ImmichLoadingOverlayController appLoader = extends HookState<ValueNotifier<bool>, _LoadingOverlay> {
ImmichLoadingOverlayController(); late final _isProcessing = ValueNotifier(false)..addListener(_listener);
ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false); OverlayEntry? overlayEntry;
ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');
void show() { void _listener() {
loaderShowingNotifier.value = true; setState(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_isProcessing.value) {
overlayEntry?.remove();
overlayEntry = _loadingEntry;
Overlay.of(context).insert(_loadingEntry);
} else {
overlayEntry?.remove();
overlayEntry = null;
}
});
});
} }
void hide() { @override
loaderShowingNotifier.value = false; ValueNotifier<bool> build(BuildContext context) {
return _isProcessing;
} }
@override
void dispose() {
_isProcessing.dispose();
super.dispose();
}
@override
Object? get debugValue => _isProcessing.value;
@override
String get debugLabel => 'useProcessingOverlay<>';
} }

View File

@ -48,8 +48,8 @@ ThemeData immichLightTheme = ThemeData(
), ),
backgroundColor: Colors.white, backgroundColor: Colors.white,
), ),
appBarTheme: AppBarTheme( appBarTheme: const AppBarTheme(
titleTextStyle: const TextStyle( titleTextStyle: TextStyle(
fontFamily: 'Overpass', fontFamily: 'Overpass',
color: Colors.indigo, color: Colors.indigo,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -61,7 +61,7 @@ ThemeData immichLightTheme = ThemeData(
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
centerTitle: true, centerTitle: true,
), ),
bottomNavigationBarTheme: BottomNavigationBarThemeData( bottomNavigationBarTheme: const BottomNavigationBarThemeData(
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
backgroundColor: immichBackgroundColor, backgroundColor: immichBackgroundColor,
selectedItemColor: Colors.indigo, selectedItemColor: Colors.indigo,
@ -69,7 +69,7 @@ ThemeData immichLightTheme = ThemeData(
cardTheme: const CardTheme( cardTheme: const CardTheme(
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
), ),
drawerTheme: DrawerThemeData( drawerTheme: const DrawerThemeData(
backgroundColor: immichBackgroundColor, backgroundColor: immichBackgroundColor,
), ),
textTheme: const TextTheme( textTheme: const TextTheme(
@ -162,7 +162,7 @@ ThemeData immichDarkTheme = ThemeData(
hintColor: Colors.grey[600], hintColor: Colors.grey[600],
fontFamily: 'Overpass', fontFamily: 'Overpass',
snackBarTheme: SnackBarThemeData( snackBarTheme: SnackBarThemeData(
contentTextStyle: TextStyle( contentTextStyle: const TextStyle(
fontFamily: 'Overpass', fontFamily: 'Overpass',
color: immichDarkThemePrimaryColor, color: immichDarkThemePrimaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -174,35 +174,35 @@ ThemeData immichDarkTheme = ThemeData(
foregroundColor: immichDarkThemePrimaryColor, foregroundColor: immichDarkThemePrimaryColor,
), ),
), ),
appBarTheme: AppBarTheme( appBarTheme: const AppBarTheme(
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
fontFamily: 'Overpass', fontFamily: 'Overpass',
color: immichDarkThemePrimaryColor, color: immichDarkThemePrimaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 18, fontSize: 18,
), ),
backgroundColor: const Color.fromARGB(255, 32, 33, 35), backgroundColor: Color.fromARGB(255, 32, 33, 35),
foregroundColor: immichDarkThemePrimaryColor, foregroundColor: immichDarkThemePrimaryColor,
elevation: 0, elevation: 0,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
centerTitle: true, centerTitle: true,
), ),
bottomNavigationBarTheme: BottomNavigationBarThemeData( bottomNavigationBarTheme: const BottomNavigationBarThemeData(
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
backgroundColor: const Color.fromARGB(255, 35, 36, 37), backgroundColor: Color.fromARGB(255, 35, 36, 37),
selectedItemColor: immichDarkThemePrimaryColor, selectedItemColor: immichDarkThemePrimaryColor,
), ),
drawerTheme: DrawerThemeData( drawerTheme: DrawerThemeData(
backgroundColor: immichDarkBackgroundColor, backgroundColor: immichDarkBackgroundColor,
scrimColor: Colors.white.withOpacity(0.1), scrimColor: Colors.white.withOpacity(0.1),
), ),
textTheme: TextTheme( textTheme: const TextTheme(
displayLarge: const TextStyle( displayLarge: TextStyle(
fontSize: 26, fontSize: 26,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 255, 255, 255), color: Color.fromARGB(255, 255, 255, 255),
), ),
displayMedium: const TextStyle( displayMedium: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 255, 255, 255), color: Color.fromARGB(255, 255, 255, 255),
@ -212,15 +212,15 @@ ThemeData immichDarkTheme = ThemeData(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: immichDarkThemePrimaryColor, color: immichDarkThemePrimaryColor,
), ),
titleSmall: const TextStyle( titleSmall: TextStyle(
fontSize: 16.0, fontSize: 16.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
titleMedium: const TextStyle( titleMedium: TextStyle(
fontSize: 18.0, fontSize: 18.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
titleLarge: const TextStyle( titleLarge: TextStyle(
fontSize: 26.0, fontSize: 26.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -258,7 +258,7 @@ ThemeData immichDarkTheme = ThemeData(
dialogTheme: const DialogTheme( dialogTheme: const DialogTheme(
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
), ),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: const InputDecorationTheme(
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: immichDarkThemePrimaryColor, color: immichDarkThemePrimaryColor,
@ -267,12 +267,12 @@ ThemeData immichDarkTheme = ThemeData(
labelStyle: TextStyle( labelStyle: TextStyle(
color: immichDarkThemePrimaryColor, color: immichDarkThemePrimaryColor,
), ),
hintStyle: const TextStyle( hintStyle: TextStyle(
fontSize: 14.0, fontSize: 14.0,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
textSelectionTheme: TextSelectionThemeData( textSelectionTheme: const TextSelectionThemeData(
cursorColor: immichDarkThemePrimaryColor, cursorColor: immichDarkThemePrimaryColor,
), ),
); );