From 144c8faf7033e5601d9cdf285bee8ebc3353cb0c Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sat, 5 Oct 2024 16:29:51 +0200 Subject: [PATCH 1/5] [Feature] Replace go_router with auto_route --- build.yaml | 18 +- lib/main.dart | 36 +- lib/models/item_base_model.dart | 6 +- lib/providers/session_info_provider.g.dart | 2 +- .../sync/background_download_provider.g.dart | 2 +- lib/routes/app_routes.dart | 121 ----- lib/routes/auto_router.dart | 135 ++++++ lib/routes/auto_router.gr.dart | 445 ++++++++++++++++++ lib/routes/build_routes/home_routes.dart | 182 ------- lib/routes/build_routes/route_builder.dart | 124 ----- lib/routes/build_routes/settings_routes.dart | 103 ---- lib/routes/nested_details_screen.dart | 70 +++ lib/screens/dashboard/dashboard_screen.dart | 23 +- .../details_screens/book_detail_screen.dart | 4 +- .../episode_detail_screen.dart | 4 +- .../details_screens/movie_detail_screen.dart | 4 +- .../details_screens/series_detail_screen.dart | 4 +- lib/screens/favourites/favourites_screen.dart | 12 +- lib/screens/{home.dart => home_screen.dart} | 51 +- .../library_search/library_search_screen.dart | 62 ++- lib/screens/login/lock_screen.dart | 20 +- lib/screens/login/login_screen.dart | 24 +- .../settings/client_settings_page.dart | 64 ++- .../settings/player_settings_page.dart | 2 + .../settings/security_settings_page.dart | 2 + lib/screens/settings/settings_scaffold.dart | 13 +- lib/screens/settings/settings_screen.dart | 327 +++++++------ lib/screens/shared/detail_scaffold.dart | 24 +- lib/screens/shared/floating_search_bar.dart | 11 +- lib/screens/shared/nested_sliver_appbar.dart | 9 +- lib/screens/splash_screen.dart | 24 +- lib/screens/syncing/synced_screen.dart | 8 +- lib/util/adaptive_layout.dart | 29 +- .../components/destination_model.dart | 4 +- .../components/fladder_appbar.dart | 4 +- .../components/navigation_body.dart | 9 +- .../components/navigation_drawer.dart | 24 +- .../components/settings_user_icon.dart | 12 +- .../navigation_scaffold.dart | 29 +- lib/wrappers/media_control_wrapper.dart | 14 +- pubspec.lock | 97 ++-- pubspec.yaml | 3 +- 42 files changed, 1181 insertions(+), 980 deletions(-) delete mode 100644 lib/routes/app_routes.dart create mode 100644 lib/routes/auto_router.dart create mode 100644 lib/routes/auto_router.gr.dart delete mode 100644 lib/routes/build_routes/home_routes.dart delete mode 100644 lib/routes/build_routes/route_builder.dart delete mode 100644 lib/routes/build_routes/settings_routes.dart create mode 100644 lib/routes/nested_details_screen.dart rename lib/screens/{home.dart => home_screen.dart} (67%) diff --git a/build.yaml b/build.yaml index 27608e5..18bfb55 100644 --- a/build.yaml +++ b/build.yaml @@ -2,13 +2,27 @@ targets: $default: sources: - lib/$lib$ - - "**/models/**.dart" # Matches models folder at any depth in lib - - "**/providers/**.dart" # Matches providers folder at any depth in lib + - "**/models/**.dart" + - "**/providers/**.dart" - lib/util/**.dart - lib/jellyfin/**.dart + - "**/**_screen.dart" + - "**/**_page.dart" - swagger/** + - lib/routes/auto_router.dart - $package$ builders: + auto_route_generator:auto_router_generator: + options: + enable_cached_builds: true + generate_for: + - lib/routes/auto_router.dart + auto_route_generator:auto_route_generator: + options: + enable_cached_builds: true + generate_for: + - "**/**_screen.dart" + - "**/**_page.dart" freezed|freezed: options: generate_for: diff --git a/lib/main.dart b/lib/main.dart index c6dd8a5..8573abc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,21 +1,15 @@ import 'dart:io'; import 'dart:ui'; -import 'package:collection/collection.dart'; -import 'package:fladder/models/syncing/i_synced_item.dart'; -import 'package:fladder/providers/settings/client_settings_provider.dart'; -import 'package:fladder/providers/sync_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:collection/collection.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -// ignore: depend_on_referenced_packages -import 'package:flutter_web_plugins/url_strategy.dart'; -import 'package:go_router/go_router.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:media_kit/media_kit.dart'; @@ -23,21 +17,23 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:universal_html/html.dart' as html; import 'package:window_manager/window_manager.dart'; import 'package:fladder/models/account_model.dart'; +import 'package:fladder/models/syncing/i_synced_item.dart'; +import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/shared_provider.dart'; +import 'package:fladder/providers/sync_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; -import 'package:fladder/routes/app_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/login/lock_screen.dart'; import 'package:fladder/theme.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/application_info.dart'; import 'package:fladder/util/string_extensions.dart'; import 'package:fladder/util/themes_data.dart'; -import 'package:universal_html/html.dart' as html; bool get _isDesktop { if (kIsWeb) return false; @@ -63,9 +59,7 @@ class CustomCacheManager { void main() async { if (kIsWeb) { - usePathUrlStrategy(); html.document.onContextMenu.listen((event) => event.preventDefault()); - GoRouter.optionURLReflectsImperativeAPIs = true; } _setupLogging(); WidgetsFlutterBinding.ensureInitialized(); @@ -111,7 +105,7 @@ void main() async { )) ], child: AdaptiveLayoutBuilder( - fallBack: LayoutState.phone, + fallBack: LayoutState.tablet, layoutPoints: [ LayoutPoints(start: 0, end: 599, type: LayoutState.phone), LayoutPoints(start: 600, end: 1919, type: LayoutState.tablet), @@ -179,7 +173,7 @@ class _MainState extends ConsumerState
with WindowListener, WidgetsBinding await ref.read(videoPlayerProvider).pause(); if (context.mounted) { - AdaptiveLayout.of(context).router.push(LockScreenRoute().route); + AdaptiveLayout.of(context).router.push(const LockRoute()); } } } @@ -316,22 +310,12 @@ class _MainState extends ConsumerState
with WindowListener, WidgetsBinding ), ), themeMode: themeMode, - routerConfig: AdaptiveLayout.of(context).router, + routerConfig: AdaptiveLayout.routerOf(context).config(), ), ); }), ); } - - List getRoutes(LayoutState state) { - switch (state) { - case LayoutState.phone: - return AppRoutes.linearRoutes; - case LayoutState.tablet: - case LayoutState.desktop: - return AppRoutes.nestedRoutes; - } - } } final currentTitleProvider = StateProvider((ref) { diff --git a/lib/models/item_base_model.dart b/lib/models/item_base_model.dart index 7b229dc..79d83d2 100644 --- a/lib/models/item_base_model.dart +++ b/lib/models/item_base_model.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:dart_mappable/dart_mappable.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/models/book_model.dart'; @@ -5,8 +6,7 @@ import 'package:fladder/models/boxset_model.dart'; import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/library_search/library_search_options.dart'; import 'package:fladder/models/playlist_model.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/details_screens/book_detail_screen.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/string_extensions.dart'; @@ -166,7 +166,7 @@ class ItemBaseModel with ItemBaseModelMappable { } } - Future navigateTo(BuildContext context) async => context.routePush(DetailsRoute(id: id), extra: this); + Future navigateTo(BuildContext context) async => context.router.push(DetailsRoute(id: id, item: this)); factory ItemBaseModel.fromBaseDto(dto.BaseItemDto item, Ref ref) { return switch (item.type) { diff --git a/lib/providers/session_info_provider.g.dart b/lib/providers/session_info_provider.g.dart index a326382..f59414e 100644 --- a/lib/providers/session_info_provider.g.dart +++ b/lib/providers/session_info_provider.g.dart @@ -27,7 +27,7 @@ Map _$$SessionInfoModelImplToJson( // RiverpodGenerator // ************************************************************************** -String _$sessionInfoHash() => r'ab5afcada1c9677cadda954c9abf7eb361dc057d'; +String _$sessionInfoHash() => r'024da7f8d05fb98f6e2e5395ed06c1cc9d003f79'; /// See also [SessionInfo]. @ProviderFor(SessionInfo) diff --git a/lib/providers/sync/background_download_provider.g.dart b/lib/providers/sync/background_download_provider.g.dart index f484457..32097e1 100644 --- a/lib/providers/sync/background_download_provider.g.dart +++ b/lib/providers/sync/background_download_provider.g.dart @@ -7,7 +7,7 @@ part of 'background_download_provider.dart'; // ************************************************************************** String _$backgroundDownloaderHash() => - r'2bc7a06682cdcfa9a754dce9b7f7ea48f873682e'; + r'9a9f91504ae4532ab37290ee9372d2e7a18380a9'; /// See also [backgroundDownloader]. @ProviderFor(backgroundDownloader) diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart deleted file mode 100644 index 10824d9..0000000 --- a/lib/routes/app_routes.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:fladder/providers/shared_provider.dart'; -import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; -import 'package:fladder/screens/home.dart'; -import 'package:fladder/screens/settings/settings_screen.dart'; -import 'package:fladder/util/adaptive_layout.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; - -class AppRoutes { - static final parentKey = GlobalKey(); - static final homeShellKey = GlobalKey(); - static final settingsKey = GlobalKey(); - static final loginRoute = LoginRoute(); - static final splashRoute = SplashRoute(); - - static final scrollController = ScrollController(); - - static GoRouter routes({required WidgetRef ref, required ScreenLayout screenLayout}) { - return GoRouter( - navigatorKey: parentKey, - initialLocation: splashRoute.route, - //Only useful for web if the user is not in an active session yet - redirect: kIsWeb - ? (context, state) async { - if (state.uri.toString() == loginRoute.route || state.uri.toString() == splashRoute.route) return null; - await Future.microtask(() { - final lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount(); - if (lastUsedAccount == null) return loginRoute.route; - if (ref.read(userProvider) == null) ref.read(userProvider.notifier).loginUser(lastUsedAccount); - }); - if (ref.read(userProvider) == null) { - return loginRoute.route; - } else { - return state.uri.toString(); - } - } - : null, - routes: AppRoutes().getRoutes(screenLayout), - ); - } - - List getRoutes(ScreenLayout screenLayout) { - switch (screenLayout) { - case ScreenLayout.single: - return linearRoutes; - case ScreenLayout.dual: - return nestedRoutes; - } - } - - static List linearRoutes = [ - loginRoute.go, - splashRoute.go, - LockScreenRoute().go, - ShellRoute( - navigatorKey: homeShellKey, - pageBuilder: (context, state, child) { - return NoTransitionPage( - child: Home( - key: state.pageKey, - location: state.uri.toString(), - nestedChild: child, - ), - ); - }, - routes: [ - DashboardRoute().go, - FavouritesRoute().go, - SyncRoute().go, - ], - ), - DetailsRoute(navKey: parentKey).go, - LibrarySearchRoute(navKey: parentKey).go, - - // Settings routes - SettingsRoute(navKey: parentKey).go, - ClientSettingsRoute(navKey: parentKey).go, - SecuritySettingsRoute(navKey: parentKey).go, - PlayerSettingsRoute(navKey: parentKey).go, - ]; - - static List nestedRoutes = [ - loginRoute.go, - splashRoute.go, - LockScreenRoute().go, - ShellRoute( - navigatorKey: homeShellKey, - pageBuilder: (context, state, child) => NoTransitionPage( - child: Home( - key: state.pageKey, - location: state.uri.toString(), - nestedChild: child, - ), - ), - routes: [ - ShellRoute( - navigatorKey: settingsKey, - pageBuilder: (context, state, child) => NoTransitionPage( - key: state.pageKey, - child: SettingsScreen(location: state.uri.toString(), child: child), - ), - routes: [ - ClientSettingsRoute(navKey: settingsKey).go, - SecuritySettingsRoute(navKey: settingsKey).go, - PlayerSettingsRoute(navKey: settingsKey).go, - ], - ), - DashboardRoute().go, - FavouritesRoute().go, - SyncRoute().go, - DetailsRoute(navKey: homeShellKey).go, - LibrarySearchRoute(navKey: homeShellKey).go, - ], - ), - ]; -} diff --git a/lib/routes/auto_router.dart b/lib/routes/auto_router.dart new file mode 100644 index 0000000..9770c28 --- /dev/null +++ b/lib/routes/auto_router.dart @@ -0,0 +1,135 @@ +import 'package:flutter/foundation.dart'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:fladder/providers/user_provider.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; +import 'package:fladder/screens/login/lock_screen.dart'; +import 'package:fladder/util/adaptive_layout.dart'; + +@AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route') +class AutoRouter extends RootStackRouter { + AutoRouter({required this.layout, required this.ref}); + + final WidgetRef ref; + final ScreenLayout layout; + + @override + List get guards => [...super.guards, AuthGuard(ref: ref)]; + + @override + RouteType get defaultRouteType => kIsWeb ? const RouteType.material() : const RouteType.adaptive(); + + @override + List get routes => [ + ..._defaultRoutes, + ...(layout == ScreenLayout.dual ? desktopRoutes : mobileRoutes), + ]; + + final List mobileRoutes = [ + _homeRoute.copyWith( + children: [ + _dashboardRoute, + _favouritesRoute, + _syncedRoute, + ], + ), + AutoRoute(page: DetailsRoute.page, path: '/details'), + AutoRoute(page: LibrarySearchRoute.page, path: '/library'), + AutoRoute(page: SettingsRoute.page, path: '/settings'), + ..._settingsChildren.map( + (e) => e.copyWith(path: "/$e", initial: false), + ), + AutoRoute(page: LockRoute.page, path: '/locked'), + ]; + final List desktopRoutes = [ + _homeRoute.copyWith( + children: [ + _dashboardRoute, + _favouritesRoute, + _syncedRoute, + AutoRoute(page: DetailsRoute.page, path: 'details'), + AutoRoute(page: LibrarySearchRoute.page, path: 'library'), + AutoRoute( + page: SettingsRoute.page, + path: 'settings', + children: _settingsChildren, + ) + ], + ), + AutoRoute(page: LockRoute.page, path: '/locked'), + ]; +} + +final List _defaultRoutes = [ + AutoRoute(page: SplashRoute.page, path: '/splash'), + AutoRoute(page: LoginRoute.page, path: '/login'), +]; + +final AutoRoute _homeRoute = AutoRoute(page: HomeRoute.page, path: '/'); +final AutoRoute _dashboardRoute = CustomRoute( + page: DashboardRoute.page, + transitionsBuilder: TransitionsBuilders.fadeIn, + initial: true, + path: 'dashboard', + maintainState: false, +); +final AutoRoute _favouritesRoute = CustomRoute( + page: FavouritesRoute.page, + transitionsBuilder: TransitionsBuilders.fadeIn, + path: 'favourites', + maintainState: false, +); +final AutoRoute _syncedRoute = CustomRoute( + page: SyncedRoute.page, + transitionsBuilder: TransitionsBuilders.fadeIn, + path: 'synced', + maintainState: false, +); + +final List _settingsChildren = [ + CustomRoute( + page: ClientSettingsRoute.page, initial: true, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'client'), + CustomRoute(page: SecuritySettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'security'), + CustomRoute(page: PlayerSettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'player'), +]; + +class LockScreenGuard extends AutoRouteGuard { + final WidgetRef ref; + + const LockScreenGuard({required this.ref}); + + @override + Future onNavigation(NavigationResolver resolver, StackRouter router) async { + if (ref.read(lockScreenActiveProvider) && resolver.routeName != const LockRoute().routeName) { + router.replace(const LockRoute()); + return; + } else { + return resolver.next(true); + } + } +} + +class AuthGuard extends AutoRouteGuard { + final WidgetRef ref; + + const AuthGuard({required this.ref}); + + @override + Future onNavigation(NavigationResolver resolver, StackRouter router) async { + if (ref.read(userProvider) != null || + resolver.routeName == const LoginRoute().routeName || + resolver.routeName == SplashRoute().routeName) { + return resolver.next(true); + } + + resolver.redirect(SplashRoute(loggedIn: (value) { + if (value) { + resolver.next(true); + } else { + router.navigate(const LoginRoute()); + } + })); + } +} diff --git a/lib/routes/auto_router.gr.dart b/lib/routes/auto_router.gr.dart new file mode 100644 index 0000000..b8475c5 --- /dev/null +++ b/lib/routes/auto_router.gr.dart @@ -0,0 +1,445 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// AutoRouterGenerator +// ************************************************************************** + +// ignore_for_file: type=lint +// coverage:ignore-file + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:auto_route/auto_route.dart' as _i14; +import 'package:fladder/models/item_base_model.dart' as _i15; +import 'package:fladder/models/items/photos_model.dart' as _i18; +import 'package:fladder/models/library_search/library_search_options.dart' + as _i17; +import 'package:fladder/routes/nested_details_screen.dart' as _i3; +import 'package:fladder/screens/dashboard/dashboard_screen.dart' as _i2; +import 'package:fladder/screens/favourites/favourites_screen.dart' as _i4; +import 'package:fladder/screens/home_screen.dart' as _i5; +import 'package:fladder/screens/library_search/library_search_screen.dart' + as _i6; +import 'package:fladder/screens/login/lock_screen.dart' as _i7; +import 'package:fladder/screens/login/login_screen.dart' as _i8; +import 'package:fladder/screens/settings/client_settings_page.dart' as _i1; +import 'package:fladder/screens/settings/player_settings_page.dart' as _i9; +import 'package:fladder/screens/settings/security_settings_page.dart' as _i10; +import 'package:fladder/screens/settings/settings_screen.dart' as _i11; +import 'package:fladder/screens/splash_screen.dart' as _i12; +import 'package:fladder/screens/syncing/synced_screen.dart' as _i13; +import 'package:flutter/material.dart' as _i16; + +/// generated route for +/// [_i1.ClientSettingsPage] +class ClientSettingsRoute extends _i14.PageRouteInfo { + const ClientSettingsRoute({List<_i14.PageRouteInfo>? children}) + : super( + ClientSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'ClientSettingsRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i1.ClientSettingsPage(); + }, + ); +} + +/// generated route for +/// [_i2.DashboardScreen] +class DashboardRoute extends _i14.PageRouteInfo { + const DashboardRoute({List<_i14.PageRouteInfo>? children}) + : super( + DashboardRoute.name, + initialChildren: children, + ); + + static const String name = 'DashboardRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i2.DashboardScreen(); + }, + ); +} + +/// generated route for +/// [_i3.DetailsScreen] +class DetailsRoute extends _i14.PageRouteInfo { + DetailsRoute({ + String id = '', + _i15.ItemBaseModel? item, + _i16.Key? key, + List<_i14.PageRouteInfo>? children, + }) : super( + DetailsRoute.name, + args: DetailsRouteArgs( + id: id, + item: item, + key: key, + ), + rawQueryParams: {'id': id}, + initialChildren: children, + ); + + static const String name = 'DetailsRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + final queryParams = data.queryParams; + final args = data.argsAs( + orElse: () => DetailsRouteArgs( + id: queryParams.getString( + 'id', + '', + ))); + return _i3.DetailsScreen( + id: args.id, + item: args.item, + key: args.key, + ); + }, + ); +} + +class DetailsRouteArgs { + const DetailsRouteArgs({ + this.id = '', + this.item, + this.key, + }); + + final String id; + + final _i15.ItemBaseModel? item; + + final _i16.Key? key; + + @override + String toString() { + return 'DetailsRouteArgs{id: $id, item: $item, key: $key}'; + } +} + +/// generated route for +/// [_i4.FavouritesScreen] +class FavouritesRoute extends _i14.PageRouteInfo { + const FavouritesRoute({List<_i14.PageRouteInfo>? children}) + : super( + FavouritesRoute.name, + initialChildren: children, + ); + + static const String name = 'FavouritesRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i4.FavouritesScreen(); + }, + ); +} + +/// generated route for +/// [_i5.HomeScreen] +class HomeRoute extends _i14.PageRouteInfo { + const HomeRoute({List<_i14.PageRouteInfo>? children}) + : super( + HomeRoute.name, + initialChildren: children, + ); + + static const String name = 'HomeRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i5.HomeScreen(); + }, + ); +} + +/// generated route for +/// [_i6.LibrarySearchScreen] +class LibrarySearchRoute extends _i14.PageRouteInfo { + LibrarySearchRoute({ + String? viewModelId, + List? folderId, + bool? favourites, + _i17.SortingOrder? sortOrder, + _i17.SortingOptions? sortingOptions, + _i18.PhotoModel? photoToView, + _i16.Key? key, + List<_i14.PageRouteInfo>? children, + }) : super( + LibrarySearchRoute.name, + args: LibrarySearchRouteArgs( + viewModelId: viewModelId, + folderId: folderId, + favourites: favourites, + sortOrder: sortOrder, + sortingOptions: sortingOptions, + photoToView: photoToView, + key: key, + ), + rawQueryParams: { + 'parentId': viewModelId, + 'folderId': folderId, + 'favourites': favourites, + 'sortOrder': sortOrder, + 'sortOptions': sortingOptions, + }, + initialChildren: children, + ); + + static const String name = 'LibrarySearchRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + final queryParams = data.queryParams; + final args = data.argsAs( + orElse: () => LibrarySearchRouteArgs( + viewModelId: queryParams.optString('parentId'), + folderId: queryParams.optList('folderId'), + favourites: queryParams.optBool('favourites'), + sortOrder: queryParams.get('sortOrder'), + sortingOptions: queryParams.get('sortOptions'), + )); + return _i6.LibrarySearchScreen( + viewModelId: args.viewModelId, + folderId: args.folderId, + favourites: args.favourites, + sortOrder: args.sortOrder, + sortingOptions: args.sortingOptions, + photoToView: args.photoToView, + key: args.key, + ); + }, + ); +} + +class LibrarySearchRouteArgs { + const LibrarySearchRouteArgs({ + this.viewModelId, + this.folderId, + this.favourites, + this.sortOrder, + this.sortingOptions, + this.photoToView, + this.key, + }); + + final String? viewModelId; + + final List? folderId; + + final bool? favourites; + + final _i17.SortingOrder? sortOrder; + + final _i17.SortingOptions? sortingOptions; + + final _i18.PhotoModel? photoToView; + + final _i16.Key? key; + + @override + String toString() { + return 'LibrarySearchRouteArgs{viewModelId: $viewModelId, folderId: $folderId, favourites: $favourites, sortOrder: $sortOrder, sortingOptions: $sortingOptions, photoToView: $photoToView, key: $key}'; + } +} + +/// generated route for +/// [_i7.LockScreen] +class LockRoute extends _i14.PageRouteInfo { + const LockRoute({List<_i14.PageRouteInfo>? children}) + : super( + LockRoute.name, + initialChildren: children, + ); + + static const String name = 'LockRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i7.LockScreen(); + }, + ); +} + +/// generated route for +/// [_i8.LoginScreen] +class LoginRoute extends _i14.PageRouteInfo { + const LoginRoute({List<_i14.PageRouteInfo>? children}) + : super( + LoginRoute.name, + initialChildren: children, + ); + + static const String name = 'LoginRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i8.LoginScreen(); + }, + ); +} + +/// generated route for +/// [_i9.PlayerSettingsPage] +class PlayerSettingsRoute extends _i14.PageRouteInfo { + const PlayerSettingsRoute({List<_i14.PageRouteInfo>? children}) + : super( + PlayerSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'PlayerSettingsRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i9.PlayerSettingsPage(); + }, + ); +} + +/// generated route for +/// [_i10.SecuritySettingsPage] +class SecuritySettingsRoute extends _i14.PageRouteInfo { + const SecuritySettingsRoute({List<_i14.PageRouteInfo>? children}) + : super( + SecuritySettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'SecuritySettingsRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i10.SecuritySettingsPage(); + }, + ); +} + +/// generated route for +/// [_i11.SettingsScreen] +class SettingsRoute extends _i14.PageRouteInfo { + const SettingsRoute({List<_i14.PageRouteInfo>? children}) + : super( + SettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'SettingsRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + return const _i11.SettingsScreen(); + }, + ); +} + +/// generated route for +/// [_i12.SplashScreen] +class SplashRoute extends _i14.PageRouteInfo { + SplashRoute({ + dynamic Function(bool)? loggedIn, + _i16.Key? key, + List<_i14.PageRouteInfo>? children, + }) : super( + SplashRoute.name, + args: SplashRouteArgs( + loggedIn: loggedIn, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'SplashRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + final args = + data.argsAs(orElse: () => const SplashRouteArgs()); + return _i12.SplashScreen( + loggedIn: args.loggedIn, + key: args.key, + ); + }, + ); +} + +class SplashRouteArgs { + const SplashRouteArgs({ + this.loggedIn, + this.key, + }); + + final dynamic Function(bool)? loggedIn; + + final _i16.Key? key; + + @override + String toString() { + return 'SplashRouteArgs{loggedIn: $loggedIn, key: $key}'; + } +} + +/// generated route for +/// [_i13.SyncedScreen] +class SyncedRoute extends _i14.PageRouteInfo { + SyncedRoute({ + _i16.ScrollController? navigationScrollController, + _i16.Key? key, + List<_i14.PageRouteInfo>? children, + }) : super( + SyncedRoute.name, + args: SyncedRouteArgs( + navigationScrollController: navigationScrollController, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'SyncedRoute'; + + static _i14.PageInfo page = _i14.PageInfo( + name, + builder: (data) { + final args = + data.argsAs(orElse: () => const SyncedRouteArgs()); + return _i13.SyncedScreen( + navigationScrollController: args.navigationScrollController, + key: args.key, + ); + }, + ); +} + +class SyncedRouteArgs { + const SyncedRouteArgs({ + this.navigationScrollController, + this.key, + }); + + final _i16.ScrollController? navigationScrollController; + + final _i16.Key? key; + + @override + String toString() { + return 'SyncedRouteArgs{navigationScrollController: $navigationScrollController, key: $key}'; + } +} diff --git a/lib/routes/build_routes/home_routes.dart b/lib/routes/build_routes/home_routes.dart deleted file mode 100644 index 88b7daf..0000000 --- a/lib/routes/build_routes/home_routes.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'package:animations/animations.dart'; -import 'package:collection/collection.dart'; -import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart'; -import 'package:fladder/models/item_base_model.dart'; -import 'package:fladder/models/library_search/library_search_options.dart'; -import 'package:fladder/routes/app_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/screens/dashboard/dashboard_screen.dart'; -import 'package:fladder/screens/syncing/synced_screen.dart'; -import 'package:fladder/screens/favourites/favourites_screen.dart'; -import 'package:fladder/screens/library_search/library_search_screen.dart'; -import 'package:fladder/screens/shared/detail_scaffold.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -class DashboardRoute extends CustomRoute { - DashboardRoute() : super(name: 'Dashboard', basePath: '/dashboard'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: AppRoutes.homeShellKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: DashboardScreen( - key: Key(basePath), - navigationScrollController: AppRoutes.scrollController, - ), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }); - }, - ); -} - -class FavouritesRoute extends CustomRoute { - FavouritesRoute() : super(name: 'Favorites', basePath: '/favorites'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: AppRoutes.homeShellKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: FavouritesScreen( - key: Key(basePath), - navigationScrollController: AppRoutes.scrollController, - ), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }); - }, - ); -} - -class SyncRoute extends CustomRoute { - SyncRoute() : super(name: 'Sync', basePath: '/sync'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: AppRoutes.homeShellKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: SyncedScreen( - key: Key(basePath), - navigationScrollController: AppRoutes.scrollController, - ), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }); - }, - ); -} - -class DetailsRoute extends CustomRoute { - final String? id; - final GlobalKey? navKey; - DetailsRoute({this.id, this.navKey}) : super(name: 'Details', basePath: '/details', arguments: ':itemId'); - - @override - String get route => "$basePath/$id"; - - @override - GoRoute get go => GoRoute( - path: path, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - final String id = state.pathParameters['itemId'] ?? "nothing"; - ItemBaseModel? item = state.extra as ItemBaseModel?; - return CustomTransitionPage( - key: state.pageKey, - child: DetailScreen( - key: Key(id), - id: id, - item: item, - ), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeThroughTransition( - animation: animation, - secondaryAnimation: secondaryAnimation, - child: child, - ); - }, - ); - }, - ); -} - -const _libraryKey = "libraryId"; -const _sortOptionsKey = "sortOptions"; -const _sortOrderKey = "sortOrder"; -const _favoriteKey = "favorite"; -const _folderKey = "folder"; - -class LibrarySearchRoute extends CustomRoute { - final String? id; - final bool? favorites; - final SortingOptions? sortOptions; - final SortOrder? sortOrder; - final String? folderId; - final GlobalKey? navKey; - LibrarySearchRoute({this.id, this.favorites, this.sortOptions, this.sortOrder, this.folderId, this.navKey}) - : super( - name: 'LibrarySearch', - basePath: '/library', - queryParameters: { - _libraryKey: id, - _sortOptionsKey: sortOptions?.name, - _sortOrderKey: sortOrder?.name, - _favoriteKey: favorites, - _folderKey: folderId, - }, - ); - - @override - String get route => "$basePath${parseUrlParameters(queryParameters)}"; - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - final String? id = state.uri.queryParameters[_libraryKey]; - final bool? favourites = bool.tryParse(state.uri.queryParameters[_favoriteKey] ?? ""); - final String? folderId = state.uri.queryParameters[_folderKey]; - final SortingOptions? sortingOptions = SortingOptions.values - .firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOptionsKey]); - final SortingOrder? sortOrder = SortingOrder.values - .firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOrderKey]); - return CustomTransitionPage( - key: state.pageKey, - child: LibrarySearchScreen( - key: Key(id ?? "librarySearch"), - viewModelId: id, - folderId: folderId?.split(','), - sortingOptions: sortingOptions, - sortOrder: sortOrder, - favourites: favourites, - ), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }, - ); - }, - ); -} diff --git a/lib/routes/build_routes/route_builder.dart b/lib/routes/build_routes/route_builder.dart deleted file mode 100644 index 5e82a3b..0000000 --- a/lib/routes/build_routes/route_builder.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:fladder/screens/login/login_screen.dart'; -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; - -import 'package:fladder/routes/app_routes.dart'; -import 'package:fladder/screens/login/lock_screen.dart'; -import 'package:fladder/screens/splash_screen.dart'; -import 'package:fladder/util/adaptive_layout.dart'; - -extension RouteContextBuilder on BuildContext { - /// Push a location onto the page stack. - Future routePush(CustomRoute route, {Object? extra}) async { - return push(route.route, extra: extra); - } - - /// Replaces the top-most page of the page stack with the given URL location - void routeReplace(CustomRoute route, {Object? extra}) { - replace(route.route, extra: extra); - } - - /// Navigate to a location. - void routeGo(CustomRoute route, {Object? extra}) { - go(route.route, extra: extra); - } - - /// [Pushed] if nested(single) else [Go] - void routePushOrGo(CustomRoute route, {Object? extra}) { - switch (AdaptiveLayout.of(this).size) { - case ScreenLayout.single: - routePush(route, extra: extra); - break; - case ScreenLayout.dual: - routeGo(route, extra: extra); - break; - } - } - - /// [Push] if nested(single) else [Replace] - void routeReplaceOrPush(CustomRoute route, {Object? extra}) { - switch (AdaptiveLayout.of(this).size) { - case ScreenLayout.single: - routePush(route, extra: extra); - break; - case ScreenLayout.dual: - routeReplace(route, extra: extra); - break; - } - } -} - -abstract class CustomRoute { - final String name; - final String basePath; - final String? arguments; - final Map? queryParameters; - - CustomRoute({required this.name, required this.basePath, this.arguments, this.queryParameters}) - : assert(basePath.isNotEmpty, 'GoRoute path cannot be empty'); - - String get route => basePath; - - String get path => "$basePath/${arguments ?? queryParameters}"; - - void navigate() { - AppRoutes.parentKey.currentContext?.routeGo(this); - } - - void replaceRoot() { - AppRoutes.parentKey.currentContext?.routeReplace(this); - } - - RouteBase get go; -} - -class LoginRoute extends CustomRoute { - LoginRoute() : super(name: 'Login', basePath: '/login'); - - @override - GoRoute get go => GoRoute( - path: basePath, - builder: (context, state) { - return const LoginScreen(); - }, - ); -} - -class SplashRoute extends CustomRoute { - SplashRoute() : super(name: 'Splash', basePath: '/splash'); - - @override - GoRoute get go => GoRoute( - path: basePath, - builder: (context, state) { - return const SplashScreen(); - }, - ); -} - -class LockScreenRoute extends CustomRoute { - final bool selfLock; - LockScreenRoute({this.selfLock = false}) : super(name: 'Lock', basePath: '/lock'); - - @override - GoRoute get go => GoRoute( - parentNavigatorKey: AppRoutes.parentKey, - path: basePath, - builder: (context, state) { - return LockScreen( - selfLock: selfLock, - ); - }, - ); -} - -String parseUrlParameters(Map? parameters) { - if (parameters == null) return ''; - String parameterString = '?'; - for (String key in parameters.keys) { - if (parameters[key] != null) parameterString += '$key=${parameters[key]}&'; - } - - return parameterString.substring(0, parameterString.length - 1); -} diff --git a/lib/routes/build_routes/settings_routes.dart b/lib/routes/build_routes/settings_routes.dart deleted file mode 100644 index 7bda1b9..0000000 --- a/lib/routes/build_routes/settings_routes.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/screens/settings/client_settings_page.dart'; -import 'package:fladder/screens/settings/player_settings_page.dart'; -import 'package:fladder/screens/settings/settings_screen.dart'; -import 'package:fladder/screens/settings/security_settings_page.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -class SettingsRoute extends CustomRoute { - final String? id; - final GlobalKey? navKey; - SettingsRoute({this.id, this.navKey}) : super(name: 'Settings', basePath: '/settings'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: const SettingsScreen(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }, - ); - }, - ); -} - -class ClientSettingsRoute extends CustomRoute { - final String? id; - final GlobalKey? navKey; - ClientSettingsRoute({this.id, this.navKey}) : super(name: 'ClientSettings', basePath: '/settings/client'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: const ClientSettingsPage(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }, - ); - }, - ); -} - -class SecuritySettingsRoute extends CustomRoute { - final String? id; - final GlobalKey? navKey; - SecuritySettingsRoute({this.id, this.navKey}) : super(name: 'SecuritySettings', basePath: '/settings/security'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: const SecuritySettingsPage(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }, - ); - }, - ); -} - -class PlayerSettingsRoute extends CustomRoute { - final String? id; - final GlobalKey? navKey; - PlayerSettingsRoute({this.id, this.navKey}) : super(name: 'PlayerSettings', basePath: '/settings/player'); - - @override - GoRoute get go => GoRoute( - path: basePath, - parentNavigatorKey: navKey, - pageBuilder: (context, state) { - return CustomTransitionPage( - key: state.pageKey, - child: const PlayerSettingsPage(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), - child: child, - ); - }, - ); - }, - ); -} diff --git a/lib/routes/nested_details_screen.dart b/lib/routes/nested_details_screen.dart new file mode 100644 index 0000000..8e95c99 --- /dev/null +++ b/lib/routes/nested_details_screen.dart @@ -0,0 +1,70 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:fladder/models/item_base_model.dart'; +import 'package:fladder/providers/items/item_details_provider.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; +import 'package:fladder/util/fladder_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +@RoutePage() +class DetailsScreen extends ConsumerStatefulWidget { + final String id; + final ItemBaseModel? item; + const DetailsScreen({@QueryParam() this.id = '', this.item, super.key}); + + @override + ConsumerState createState() => _DetailsScreenState(); +} + +class _DetailsScreenState extends ConsumerState { + late Widget currentWidget = const Center( + key: Key("progress-indicator"), + child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round), + ); + + @override + void initState() { + super.initState(); + Future.microtask(() async { + if (widget.item != null) { + setState(() { + currentWidget = widget.item!.detailScreenWidget; + }); + } else { + final response = await ref.read(itemDetailsProvider.notifier).fetchDetails(widget.id); + if (context.mounted) { + if (response != null) { + setState(() { + currentWidget = response.detailScreenWidget; + }); + } else { + const DashboardRoute().navigate(context); + } + } + } + }); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Hero( + tag: widget.id, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface.withOpacity(1.0), + ), + //Small offset to match detailscaffold + child: Transform.translate( + offset: const Offset(0, -5), child: FladderImage(image: widget.item?.getPosters?.primary)), + ), + ), + AnimatedSwitcher( + duration: const Duration(seconds: 1), + child: currentWidget, + ) + ], + ); + } +} diff --git a/lib/screens/dashboard/dashboard_screen.dart b/lib/screens/dashboard/dashboard_screen.dart index 3d4269a..5e738d8 100644 --- a/lib/screens/dashboard/dashboard_screen.dart +++ b/lib/screens/dashboard/dashboard_screen.dart @@ -1,6 +1,11 @@ import 'dart:async'; +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/models/library_search/library_search_options.dart'; @@ -10,8 +15,7 @@ import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/home_settings_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/views_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/media/carousel_banner.dart'; import 'package:fladder/screens/shared/media/poster_row.dart'; import 'package:fladder/screens/shared/nested_scaffold.dart'; @@ -23,13 +27,10 @@ import 'package:fladder/util/sliver_list_padding.dart'; import 'package:fladder/widgets/shared/pinch_poster_zoom.dart'; import 'package:fladder/widgets/shared/poster_size_slider.dart'; import 'package:fladder/widgets/shared/pull_to_refresh.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +@RoutePage() class DashboardScreen extends ConsumerStatefulWidget { - final ScrollController navigationScrollController; const DashboardScreen({ - required this.navigationScrollController, super.key, }); @@ -91,8 +92,8 @@ class _DashboardScreenState extends ConsumerState { child: PinchPosterZoom( scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference), child: CustomScrollView( + controller: AdaptiveLayout.scrollOf(context), physics: const AlwaysScrollableScrollPhysics(), - controller: widget.navigationScrollController, slivers: [ if (AdaptiveLayout.of(context).layout == LayoutState.phone) NestedSliverAppBar( @@ -175,9 +176,9 @@ class _DashboardScreenState extends ConsumerState { .map((view) => SliverToBoxAdapter( child: PosterRow( label: context.localized.dashboardRecentlyAdded(view.name), - onLabelClick: () => context.routePushOrGo(LibrarySearchRoute( - id: view.id, - sortOptions: switch (view.collectionType) { + onLabelClick: () => context.router.push(LibrarySearchRoute( + viewModelId: view.id, + sortingOptions: switch (view.collectionType) { CollectionType.tvshows || CollectionType.books || CollectionType.boxsets || @@ -186,7 +187,7 @@ class _DashboardScreenState extends ConsumerState { SortingOptions.dateLastContentAdded, _ => SortingOptions.dateAdded, }, - sortOrder: SortOrder.descending, + sortOrder: SortingOrder.descending, )), posters: view.recentlyAdded, ), diff --git a/lib/screens/details_screens/book_detail_screen.dart b/lib/screens/details_screens/book_detail_screen.dart index 1db996c..b77f703 100644 --- a/lib/screens/details_screens/book_detail_screen.dart +++ b/lib/screens/details_screens/book_detail_screen.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/models/book_model.dart'; import 'package:fladder/providers/items/book_details_provider.dart'; @@ -18,7 +19,6 @@ import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; class BookDetailScreen extends ConsumerStatefulWidget { final BookModel item; @@ -47,7 +47,7 @@ class _BookDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.pop(); + context.router.maybePop(); } }, ), diff --git a/lib/screens/details_screens/episode_detail_screen.dart b/lib/screens/details_screens/episode_detail_screen.dart index 3326acd..9b88e6e 100644 --- a/lib/screens/details_screens/episode_detail_screen.dart +++ b/lib/screens/details_screens/episode_detail_screen.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/shared/media/components/media_play_button.dart'; @@ -21,7 +22,6 @@ import 'package:fladder/screens/shared/media/expanding_overview.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; -import 'package:go_router/go_router.dart'; class EpisodeDetailScreen extends ConsumerStatefulWidget { final ItemBaseModel item; @@ -52,7 +52,7 @@ class _ItemDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.pop(); + context.router.maybePop(); } }, ), diff --git a/lib/screens/details_screens/movie_detail_screen.dart b/lib/screens/details_screens/movie_detail_screen.dart index 3f093b8..a235d78 100644 --- a/lib/screens/details_screens/movie_detail_screen.dart +++ b/lib/screens/details_screens/movie_detail_screen.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/movies_details_provider.dart'; @@ -19,7 +20,6 @@ import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; class MovieDetailScreen extends ConsumerStatefulWidget { final ItemBaseModel item; @@ -49,7 +49,7 @@ class _ItemDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.pop(); + context.router.maybePop(); } }, ), diff --git a/lib/screens/details_screens/series_detail_screen.dart b/lib/screens/details_screens/series_detail_screen.dart index 135be8b..9278da9 100644 --- a/lib/screens/details_screens/series_detail_screen.dart +++ b/lib/screens/details_screens/series_detail_screen.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/shared/media/components/media_play_button.dart'; @@ -22,7 +23,6 @@ import 'package:fladder/screens/shared/media/season_row.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; -import 'package:go_router/go_router.dart'; class SeriesDetailScreen extends ConsumerStatefulWidget { final ItemBaseModel item; @@ -51,7 +51,7 @@ class _SeriesDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.pop(); + context.router.maybePop(); } }, ), diff --git a/lib/screens/favourites/favourites_screen.dart b/lib/screens/favourites/favourites_screen.dart index baf73e5..0abb420 100644 --- a/lib/screens/favourites/favourites_screen.dart +++ b/lib/screens/favourites/favourites_screen.dart @@ -1,5 +1,6 @@ +import 'package:auto_route/auto_route.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/nested_scaffold.dart'; import 'package:fladder/screens/shared/nested_sliver_appbar.dart'; import 'package:fladder/util/adaptive_layout.dart'; @@ -14,10 +15,9 @@ import 'package:fladder/screens/shared/media/poster_grid.dart'; import 'package:fladder/util/sliver_list_padding.dart'; import 'package:fladder/widgets/shared/pull_to_refresh.dart'; +@RoutePage() class FavouritesScreen extends ConsumerWidget { - final ScrollController navigationScrollController; - - const FavouritesScreen({required this.navigationScrollController, super.key}); + const FavouritesScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,13 +30,13 @@ class FavouritesScreen extends ConsumerWidget { scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference / 2), child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), - controller: navigationScrollController, + controller: AdaptiveLayout.scrollOf(context), slivers: [ if (AdaptiveLayout.of(context).layout == LayoutState.phone) NestedSliverAppBar( searchTitle: "${context.localized.search} ${context.localized.favorites.toLowerCase()}...", parent: context, - route: LibrarySearchRoute(favorites: true), + route: LibrarySearchRoute(favourites: true), ) else const DefaultSliverTopBadding(), diff --git a/lib/screens/home.dart b/lib/screens/home_screen.dart similarity index 67% rename from lib/screens/home.dart rename to lib/screens/home_screen.dart index e77d481..a72d969 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home_screen.dart @@ -1,13 +1,13 @@ -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/providers/user_provider.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; +import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/string_extensions.dart'; import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart'; import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart'; @@ -19,11 +19,9 @@ enum HomeTabs { sync; } -class Home extends ConsumerWidget { - final HomeTabs? currentTab; - final Widget? nestedChild; - final String? location; - const Home({this.currentTab, this.nestedChild, this.location, super.key}); +@RoutePage() +class HomeScreen extends ConsumerWidget { + const HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,13 +33,13 @@ class Home extends ConsumerWidget { label: context.localized.navigationDashboard, icon: const Icon(IconsaxOutline.home), selectedIcon: const Icon(IconsaxBold.home), - route: DashboardRoute(), - action: () => context.routeGo(DashboardRoute()), + route: const DashboardRoute(), + action: () => context.router.navigate(const DashboardRoute()), floatingActionButton: AdaptiveFab( context: context, title: context.localized.search, key: Key(e.name.capitalize()), - onPressed: () => context.routePushOrGo(LibrarySearchRoute()), + onPressed: () => context.router.navigate(LibrarySearchRoute()), child: const Icon(IconsaxOutline.search_normal_1), ), ); @@ -50,15 +48,15 @@ class Home extends ConsumerWidget { label: context.localized.navigationFavorites, icon: const Icon(IconsaxOutline.heart), selectedIcon: const Icon(IconsaxBold.heart), - route: FavouritesRoute(), + route: const FavouritesRoute(), floatingActionButton: AdaptiveFab( context: context, title: context.localized.filter(0), key: Key(e.name.capitalize()), - onPressed: () => context.routePushOrGo(LibrarySearchRoute(favorites: true)), + onPressed: () => context.router.navigate(LibrarySearchRoute(favourites: true)), child: const Icon(IconsaxOutline.heart_search), ), - action: () => context.routeGo(FavouritesRoute()), + action: () => context.router.navigate(const FavouritesRoute()), ); case HomeTabs.sync: if (canDownload) { @@ -66,8 +64,8 @@ class Home extends ConsumerWidget { label: context.localized.navigationSync, icon: const Icon(IconsaxOutline.cloud), selectedIcon: const Icon(IconsaxBold.cloud), - route: SyncRoute(), - action: () => context.routeGo(SyncRoute()), + route: SyncedRoute(), + action: () => context.router.navigate(SyncedRoute()), ); } return null; @@ -75,12 +73,17 @@ class Home extends ConsumerWidget { return null; } }); - - return NavigationScaffold( - currentIndex: currentTab?.index ?? 0, - location: location, - nestedChild: nestedChild, - destinations: destinations.whereNotNull().toList(), + return HeroControllerScope( + controller: HeroController(), + child: AutoRouter( + builder: (context, child) { + return NavigationScaffold( + destinations: destinations.whereNotNull().toList(), + currentRouteName: context.router.current.name, + nestedChild: child, + ); + }, + ), ); } } diff --git a/lib/screens/library_search/library_search_screen.dart b/lib/screens/library_search/library_search_screen.dart index 2a16202..6141910 100644 --- a/lib/screens/library_search/library_search_screen.dart +++ b/lib/screens/library_search/library_search_screen.dart @@ -1,6 +1,4 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -43,6 +41,10 @@ import 'package:fladder/widgets/shared/pull_to_refresh.dart'; import 'package:fladder/widgets/shared/scroll_position.dart'; import 'package:fladder/widgets/shared/shapes.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +@RoutePage() class LibrarySearchScreen extends ConsumerStatefulWidget { final String? viewModelId; final bool? favourites; @@ -51,11 +53,11 @@ class LibrarySearchScreen extends ConsumerStatefulWidget { final SortingOptions? sortingOptions; final PhotoModel? photoToView; const LibrarySearchScreen({ - this.viewModelId, - this.folderId, - this.favourites, - this.sortOrder, - this.sortingOptions, + @QueryParam("parentId") this.viewModelId, + @QueryParam("folderId") this.folderId, + @QueryParam("favourites") this.favourites, + @QueryParam("sortOrder") this.sortOrder, + @QueryParam("sortOptions") this.sortingOptions, this.photoToView, super.key, }); @@ -139,7 +141,8 @@ class _LibrarySearchScreenState extends ConsumerState { child: Scaffold( extendBody: true, extendBodyBehindAppBar: true, - floatingActionButtonLocation: playerState == VideoPlayerState.minimized ? FloatingActionButtonLocation.centerFloat : null, + floatingActionButtonLocation: + playerState == VideoPlayerState.minimized ? FloatingActionButtonLocation.centerFloat : null, floatingActionButton: switch (playerState) { VideoPlayerState.minimized => const Padding( padding: EdgeInsets.symmetric(horizontal: 8), @@ -197,8 +200,9 @@ class _LibrarySearchScreenState extends ConsumerState { child: MediaQuery.removeViewInsets( context: context, child: ClipRRect( - borderRadius: - AdaptiveLayout.of(context).layout == LayoutState.desktop ? BorderRadius.circular(15) : BorderRadius.circular(0), + borderRadius: AdaptiveLayout.of(context).layout == LayoutState.desktop + ? BorderRadius.circular(15) + : BorderRadius.circular(0), child: FladderScrollbar( visible: AdaptiveLayout.of(context).inputDevice != InputDevice.pointer, controller: scrollController, @@ -206,7 +210,8 @@ class _LibrarySearchScreenState extends ConsumerState { refreshKey: refreshKey, autoFocus: false, contextRefresh: false, - onRefresh: () async => libraryProvider.initRefresh(widget.folderId, widget.viewModelId, widget.favourites), + onRefresh: () async => + libraryProvider.initRefresh(widget.folderId, widget.viewModelId, widget.favourites), refreshOnStart: false, child: CustomScrollView( physics: const AlwaysScrollableNoImplicitScrollPhysics(), @@ -228,7 +233,8 @@ class _LibrarySearchScreenState extends ConsumerState { actions: [ const SizedBox(width: 4), Builder(builder: (context) { - final isFavorite = librarySearchResults.nestedCurrentItem?.userData.isFavourite == true; + final isFavorite = + librarySearchResults.nestedCurrentItem?.userData.isFavourite == true; final itemActions = librarySearchResults.nestedCurrentItem?.generateActions( context, ref, @@ -276,7 +282,8 @@ class _LibrarySearchScreenState extends ConsumerState { (e) => FilledButton.tonal( style: FilledButtonTheme.of(context).style?.copyWith( padding: const WidgetStatePropertyAll( - EdgeInsets.symmetric(horizontal: 12, vertical: 24)), + EdgeInsets.symmetric( + horizontal: 12, vertical: 24)), backgroundColor: WidgetStateProperty.resolveWith( (states) { if (e != currentType) { @@ -312,8 +319,8 @@ class _LibrarySearchScreenState extends ConsumerState { return Card( elevation: 0, child: Tooltip( - message: - librarySearchResults.nestedCurrentItem?.type.label(context) ?? context.localized.library(1), + message: librarySearchResults.nestedCurrentItem?.type.label(context) ?? + context.localized.library(1), child: InkWell( onTapUp: (details) async { if (AdaptiveLayout.of(context).inputDevice == InputDevice.pointer) { @@ -324,8 +331,9 @@ class _LibrarySearchScreenState extends ConsumerState { position: RelativeRect.fromLTRB(left, top, 40, 100), items: [ PopupMenuItem( - child: Text(librarySearchResults.nestedCurrentItem?.type.label(context) ?? - context.localized.library(0))), + child: Text( + librarySearchResults.nestedCurrentItem?.type.label(context) ?? + context.localized.library(0))), itemCountWidget.toPopupMenuItem(useIcons: true), refreshAction.toPopupMenuItem(useIcons: true), itemViewAction.toPopupMenuItem(useIcons: true), @@ -356,7 +364,8 @@ class _LibrarySearchScreenState extends ConsumerState { child: Icon( isFavorite ? librarySearchResults.nestedCurrentItem?.type.selectedicon - : librarySearchResults.nestedCurrentItem?.type.icon ?? IconsaxOutline.document, + : librarySearchResults.nestedCurrentItem?.type.icon ?? + IconsaxOutline.document, color: isFavorite ? Theme.of(context).colorScheme.primary : null, ), ), @@ -433,7 +442,8 @@ class _LibrarySearchScreenState extends ConsumerState { if (postersList.isNotEmpty) SliverPadding( padding: EdgeInsets.only( - left: MediaQuery.of(context).padding.left, right: MediaQuery.of(context).padding.right), + left: MediaQuery.of(context).padding.left, + right: MediaQuery.of(context).padding.right), sliver: LibraryViews( key: uniqueKey, items: postersList, @@ -568,7 +578,8 @@ class _LibrarySearchBottomBar extends ConsumerWidget { }, label: Text(context.localized.removeFromCollection), icon: Container( - decoration: BoxDecoration(color: Theme.of(context).colorScheme.onPrimary, borderRadius: BorderRadius.circular(6)), + decoration: + BoxDecoration(color: Theme.of(context).colorScheme.onPrimary, borderRadius: BorderRadius.circular(6)), child: const Padding( padding: EdgeInsets.all(3.0), child: Icon(IconsaxOutline.save_remove, size: 20), @@ -620,8 +631,8 @@ class _LibrarySearchBottomBar extends ConsumerWidget { clipBehavior: Clip.antiAlias, elevation: 0, borderRadiusGeometry: BorderRadius.circular(6), - onTap: () => - scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubic), + onTap: () => scrollController.animateTo(0, + duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubic), child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, @@ -679,8 +690,9 @@ class _LibrarySearchBottomBar extends ConsumerWidget { AnimatedFadeSize( child: librarySearchResults.selecteMode ? Container( - decoration: - BoxDecoration(color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(16)), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(16)), child: Row( children: [ Tooltip( diff --git a/lib/screens/login/lock_screen.dart b/lib/screens/login/lock_screen.dart index 1356b7f..6aec54e 100644 --- a/lib/screens/login/lock_screen.dart +++ b/lib/screens/login/lock_screen.dart @@ -1,24 +1,24 @@ -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; import 'package:fladder/models/account_model.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/login/widgets/login_icon.dart'; import 'package:fladder/screens/shared/fladder_snackbar.dart'; import 'package:fladder/screens/shared/passcode_input.dart'; import 'package:fladder/util/auth_service.dart'; +import 'package:fladder/util/localization_helper.dart'; final lockScreenActiveProvider = StateProvider((ref) => false); +@RoutePage() class LockScreen extends ConsumerStatefulWidget { - final bool selfLock; - const LockScreen({this.selfLock = false, super.key}); + const LockScreen({super.key}); @override ConsumerState createState() => _LockScreenState(); @@ -52,10 +52,6 @@ class _LockScreenState extends ConsumerState with WidgetsBindingObse WidgetsBinding.instance.addObserver(this); Future.microtask(() { ref.read(lockScreenActiveProvider.notifier).update((state) => true); - final user = ref.read(userProvider); - if (user != null && !widget.selfLock) { - tapLoggedInAccount(user); - } }); hackyFixForBlackNavbar(); } @@ -63,7 +59,7 @@ class _LockScreenState extends ConsumerState with WidgetsBindingObse void handleLogin(AccountModel user) { ref.read(lockScreenActiveProvider.notifier).update((state) => false); poppingLockScreen = true; - context.pop(); + context.router.popForced(); } void tapLoggedInAccount(AccountModel user) async { @@ -131,7 +127,7 @@ class _LockScreenState extends ConsumerState with WidgetsBindingObse ElevatedButton.icon( onPressed: () { ref.read(lockScreenActiveProvider.notifier).update((state) => false); - context.routeGo(LoginRoute()); + context.router.push(const LoginRoute()); }, icon: const Icon(Icons.login_rounded), label: Text(context.localized.login), diff --git a/lib/screens/login/login_screen.dart b/lib/screens/login/login_screen.dart index d6a7f0e..16a2fc4 100644 --- a/lib/screens/login/login_screen.dart +++ b/lib/screens/login/login_screen.dart @@ -1,34 +1,34 @@ import 'dart:async'; import 'dart:developer'; -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/screens/login/lock_screen.dart'; -import 'package:fladder/screens/login/widgets/discover_servers_widget.dart'; -import 'package:fladder/screens/shared/fladder_logo.dart'; -import 'package:fladder/util/adaptive_layout.dart'; -import 'package:fladder/util/localization_helper.dart'; -import 'package:fladder/util/string_extensions.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; import 'package:fladder/models/account_model.dart'; import 'package:fladder/providers/auth_provider.dart'; import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; +import 'package:fladder/screens/login/lock_screen.dart'; import 'package:fladder/screens/login/login_edit_user.dart'; import 'package:fladder/screens/login/login_user_grid.dart'; +import 'package:fladder/screens/login/widgets/discover_servers_widget.dart'; import 'package:fladder/screens/shared/animated_fade_size.dart'; +import 'package:fladder/screens/shared/fladder_logo.dart'; import 'package:fladder/screens/shared/fladder_snackbar.dart'; import 'package:fladder/screens/shared/outlined_text_field.dart'; import 'package:fladder/screens/shared/passcode_input.dart'; +import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/auth_service.dart'; import 'package:fladder/util/list_padding.dart'; +import 'package:fladder/util/localization_helper.dart'; +import 'package:fladder/util/string_extensions.dart'; import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart'; +@RoutePage() class LoginScreen extends ConsumerStatefulWidget { const LoginScreen({super.key}); @@ -164,7 +164,7 @@ class _LoginPageState extends ConsumerState { serverTextController.text = value; startAddingNewUser(); }); - context.pop(); + context.router.maybePop(); }, ), ); @@ -211,7 +211,7 @@ class _LoginPageState extends ConsumerState { void loggedInGoToHome() { ref.read(lockScreenActiveProvider.notifier).update((state) => false); if (context.mounted) { - context.routeGo(DashboardRoute()); + context.router.push(const DashboardRoute()); } } diff --git a/lib/screens/settings/client_settings_page.dart b/lib/screens/settings/client_settings_page.dart index b41368e..d913c6a 100644 --- a/lib/screens/settings/client_settings_page.dart +++ b/lib/screens/settings/client_settings_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; import 'package:fladder/models/settings/home_settings_model.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; @@ -13,7 +13,7 @@ import 'package:fladder/providers/settings/home_settings_provider.dart'; import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/sync_provider.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/settings_scaffold.dart'; import 'package:fladder/screens/settings/widgets/settings_label_divider.dart'; @@ -30,6 +30,7 @@ import 'package:fladder/util/theme_mode_extension.dart'; import 'package:fladder/widgets/shared/enum_selection.dart'; import 'package:fladder/widgets/shared/fladder_slider.dart'; +@RoutePage() class ClientSettingsPage extends ConsumerStatefulWidget { const ClientSettingsPage({super.key}); @@ -38,16 +39,17 @@ class ClientSettingsPage extends ConsumerStatefulWidget { } class _ClientSettingsPageState extends ConsumerState { - late final nextUpDaysEditor = - TextEditingController(text: ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff?.inDays ?? 14)).toString()); + late final nextUpDaysEditor = TextEditingController( + text: ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff?.inDays ?? 14)).toString()); - late final libraryPageSizeController = - TextEditingController(text: ref.read(clientSettingsProvider.select((value) => value.libraryPageSize))?.toString() ?? ""); + late final libraryPageSizeController = TextEditingController( + text: ref.read(clientSettingsProvider.select((value) => value.libraryPageSize))?.toString() ?? ""); @override Widget build(BuildContext context) { final clientSettings = ref.watch(clientSettingsProvider); - final showBackground = AdaptiveLayout.of(context).layout != LayoutState.phone && AdaptiveLayout.of(context).size != ScreenLayout.single; + final showBackground = AdaptiveLayout.of(context).layout != LayoutState.phone && + AdaptiveLayout.of(context).size != ScreenLayout.single; final currentFolder = ref.watch(syncProvider.notifier).savePath; Locale currentLocale = WidgetsBinding.instance.platformDispatcher.locale; @@ -72,8 +74,8 @@ class _ClientSettingsPageState extends ConsumerState { actions: [ ElevatedButton( onPressed: () async { - String? selectedDirectory = await FilePicker.platform - .getDirectoryPath(dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder); + String? selectedDirectory = await FilePicker.platform.getDirectoryPath( + dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder); if (selectedDirectory != null) { ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory); } @@ -85,8 +87,8 @@ class _ClientSettingsPageState extends ConsumerState { ), ) : () async { - String? selectedDirectory = await FilePicker.platform - .getDirectoryPath(dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder); + String? selectedDirectory = await FilePicker.platform.getDirectoryPath( + dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder); if (selectedDirectory != null) { ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory); } @@ -131,10 +133,10 @@ class _ClientSettingsPageState extends ConsumerState { (context) async { await ref.read(syncProvider.notifier).clear(); setState(() {}); - context.pop(); + context.router.maybePop(); }, context.localized.clear, - (context) => context.pop(), + (context) => context.router.maybePop(), context.localized.cancel, ); }, @@ -155,9 +157,9 @@ class _ClientSettingsPageState extends ConsumerState { initialValue: clientSettings.timeOut ?? const Duration(), ); - ref - .read(clientSettingsProvider.notifier) - .setTimeOut(timePicker != null ? Duration(minutes: timePicker.inMinutes, seconds: timePicker.inSeconds % 60) : null); + ref.read(clientSettingsProvider.notifier).setTimeOut(timePicker != null + ? Duration(minutes: timePicker.inMinutes, seconds: timePicker.inSeconds % 60) + : null); }, ), const Divider(), @@ -176,7 +178,9 @@ class _ClientSettingsPageState extends ConsumerState { (entry) => PopupMenuItem( value: entry, child: Text(entry.label(context)), - onTap: () => ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(carouselSettings: entry)), + onTap: () => ref + .read(homeSettingsProvider.notifier) + .update((context) => context.copyWith(carouselSettings: entry)), ), ) .toList(), @@ -196,7 +200,8 @@ class _ClientSettingsPageState extends ConsumerState { (entry) => PopupMenuItem( value: entry, child: Text(entry.label(context)), - onTap: () => ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)), + onTap: () => + ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)), ), ) .toList(), @@ -223,7 +228,9 @@ class _ClientSettingsPageState extends ConsumerState { fontWeight: currentLocale.languageCode == entry.languageCode ? FontWeight.bold : null, ), ), - onTap: () => ref.read(clientSettingsProvider.notifier).update((state) => state.copyWith(selectedLocale: entry)), + onTap: () => ref + .read(clientSettingsProvider.notifier) + .update((state) => state.copyWith(selectedLocale: entry)), ), ) ]; @@ -233,7 +240,8 @@ class _ClientSettingsPageState extends ConsumerState { SettingsListTile( label: Text(context.localized.settingsBlurredPlaceholderTitle), subLabel: Text(context.localized.settingsBlurredPlaceholderDesc), - onTap: () => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders), + onTap: () => + ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders), trailing: Switch( value: clientSettings.blurPlaceHolders, onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(value), @@ -242,7 +250,8 @@ class _ClientSettingsPageState extends ConsumerState { SettingsListTile( label: Text(context.localized.settingsBlurEpisodesTitle), subLabel: Text(context.localized.settingsBlurEpisodesDesc), - onTap: () => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes), + onTap: () => + ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes), trailing: Switch( value: clientSettings.blurUpcomingEpisodes, onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(value), @@ -286,8 +295,9 @@ class _ClientSettingsPageState extends ConsumerState { )), ), SettingsListTile( - label: Text( - AdaptiveLayout.of(context).isDesktop ? context.localized.settingsShowScaleSlider : context.localized.settingsPosterPinch), + label: Text(AdaptiveLayout.of(context).isDesktop + ? context.localized.settingsShowScaleSlider + : context.localized.settingsPosterPinch), onTap: () => ref.read(clientSettingsProvider.notifier).update( (current) => current.copyWith(pinchPosterZoom: !current.pinchPosterZoom), ), @@ -314,7 +324,9 @@ class _ClientSettingsPageState extends ConsumerState { max: 1.5, value: clientSettings.posterSize, divisions: 20, - onChanged: (value) => ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(posterSize: value)), + onChanged: (value) => ref + .read(clientSettingsProvider.notifier) + .update((current) => current.copyWith(posterSize: value)), ), ), const Divider(), @@ -439,14 +451,14 @@ class _ClientSettingsPageState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ FilledButton( - onPressed: () => context.pop(), + onPressed: () => context.router.maybePop(), child: Text(context.localized.cancel), ), const SizedBox(width: 8), ElevatedButton( onPressed: () async { await ref.read(sharedPreferencesProvider).clear(); - context.routeGo(LoginRoute()); + context.router.push(const LoginRoute()); }, child: Text(context.localized.clear), ) diff --git a/lib/screens/settings/player_settings_page.dart b/lib/screens/settings/player_settings_page.dart index def9a5c..9b36117 100644 --- a/lib/screens/settings/player_settings_page.dart +++ b/lib/screens/settings/player_settings_page.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:fladder/providers/settings/video_player_settings_provider.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/settings_scaffold.dart'; @@ -14,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'dart:io' show Platform; +@RoutePage() class PlayerSettingsPage extends ConsumerStatefulWidget { const PlayerSettingsPage({super.key}); diff --git a/lib/screens/settings/security_settings_page.dart b/lib/screens/settings/security_settings_page.dart index 99602c2..fc570b2 100644 --- a/lib/screens/settings/security_settings_page.dart +++ b/lib/screens/settings/security_settings_page.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/settings_scaffold.dart'; @@ -8,6 +9,7 @@ import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +@RoutePage() class SecuritySettingsPage extends ConsumerStatefulWidget { const SecuritySettingsPage({super.key}); diff --git a/lib/screens/settings/settings_scaffold.dart b/lib/screens/settings/settings_scaffold.dart index 6e7fb3c..4223bc0 100644 --- a/lib/screens/settings/settings_scaffold.dart +++ b/lib/screens/settings/settings_scaffold.dart @@ -1,8 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/shared/user_icon.dart'; import 'package:fladder/util/adaptive_layout.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class SettingsScaffold extends ConsumerWidget { final String label; @@ -36,7 +38,6 @@ class SettingsScaffold extends ConsumerWidget { slivers: [ if (AdaptiveLayout.of(context).size == ScreenLayout.single) SliverAppBar.large( - titleSpacing: 20, backgroundColor: Theme.of(context).scaffoldBackgroundColor, flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16) @@ -54,10 +55,10 @@ class SettingsScaffold extends ConsumerWidget { )) ], ), - expandedTitleScale: 2, + expandedTitleScale: 1.2, ), - expandedHeight: 175, - collapsedHeight: 100, + expandedHeight: 100, + collapsedHeight: 80, pinned: false, floating: true, ) diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 79a03a4..11675b8 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -1,27 +1,26 @@ +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:fladder/providers/auth_provider.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/settings/quick_connect_window.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/settings_scaffold.dart'; import 'package:fladder/screens/shared/animated_fade_size.dart'; import 'package:fladder/screens/shared/fladder_icon.dart'; import 'package:fladder/util/adaptive_layout.dart'; +import 'package:fladder/util/application_info.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/theme_extensions.dart'; import 'package:fladder/widgets/shared/hide_on_scroll.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:fladder/providers/auth_provider.dart'; -import 'package:fladder/util/application_info.dart'; -import 'package:go_router/go_router.dart'; +@RoutePage() class SettingsScreen extends ConsumerStatefulWidget { - final Widget? child; - final String? location; - const SettingsScreen({this.child, this.location, super.key}); + const SettingsScreen({super.key}); @override ConsumerState createState() => _SettingsScreenState(); @@ -29,27 +28,30 @@ class SettingsScreen extends ConsumerStatefulWidget { class _SettingsScreenState extends ConsumerState { final scrollController = ScrollController(); - late final singlePane = widget.child == null; final minVerticalPadding = 20.0; @override Widget build(BuildContext context) { - if (singlePane) { + if (AdaptiveLayout.of(context).size == ScreenLayout.single) { return Card( elevation: 0, child: _leftPane(context), ); } else { - return Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(flex: 1, child: _leftPane(context)), - Expanded( - flex: 2, - child: widget.child ?? Container(), - ), - ], + return AutoRouter( + builder: (context, content) { + return Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded(flex: 1, child: _leftPane(context)), + Expanded( + flex: 2, + child: content, + ), + ], + ); + }, ); } } @@ -68,152 +70,165 @@ class _SettingsScreenState extends ConsumerState { } } - bool containsRoute(CustomRoute route) => widget.location == route.route; - Widget _leftPane(BuildContext context) { - final quickConnectAvailable = ref.watch(userProvider.select((value) => value?.serverConfiguration?.quickConnectAvailable ?? false)); - return SettingsScaffold( - label: context.localized.settings, - scrollController: scrollController, - showUserIcon: true, - items: [ - if (context.canPop() && AdaptiveLayout.of(context).isDesktop) - Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: IconButton.filledTonal( - style: IconButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), - ), - onPressed: () { - context.pop(); - }, - icon: Padding( - padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4), - child: const Icon(IconsaxOutline.arrow_left_2), + void navigateTo(PageRouteInfo route) { + AdaptiveLayout.of(context).size == ScreenLayout.single + ? context.router.navigate(route) + : context.router.replace(route); + } + + bool containsRoute(PageRouteInfo route) { + return context.router.current.name == route.routeName; + } + + final quickConnectAvailable = + ref.watch(userProvider.select((value) => value?.serverConfiguration?.quickConnectAvailable ?? false)); + return Container( + color: context.colors.surface, + child: SettingsScaffold( + label: context.localized.settings, + scrollController: scrollController, + showUserIcon: true, + items: [ + if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.single) + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: IconButton.filledTonal( + style: IconButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), + ), + onPressed: () => context.router.maybePop(), + icon: Padding( + padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4), + child: const Icon(IconsaxOutline.arrow_left_2), + ), ), ), ), - ), - SettingsListTile( - label: Text(context.localized.settingsClientTitle), - subLabel: Text(context.localized.settingsClientDesc), - selected: containsRoute(ClientSettingsRoute()), - icon: deviceIcon, - onTap: () => context.routeReplaceOrPush(ClientSettingsRoute()), - ), - if (quickConnectAvailable) SettingsListTile( - label: Text(context.localized.settingsQuickConnectTitle), - icon: IconsaxOutline.password_check, - onTap: () => openQuickConnectDialog(context), + label: Text(context.localized.settingsClientTitle), + subLabel: Text(context.localized.settingsClientDesc), + selected: containsRoute(const ClientSettingsRoute()), + icon: deviceIcon, + onTap: () => navigateTo(const ClientSettingsRoute()), ), - SettingsListTile( - label: Text(context.localized.settingsProfileTitle), - subLabel: Text(context.localized.settingsProfileDesc), - selected: containsRoute(SecuritySettingsRoute()), - icon: IconsaxOutline.security_user, - onTap: () => context.routeReplaceOrPush(SecuritySettingsRoute()), - ), - SettingsListTile( - label: Text(context.localized.settingsPlayerTitle), - subLabel: Text(context.localized.settingsPlayerDesc), - selected: containsRoute(PlayerSettingsRoute()), - icon: IconsaxOutline.video_play, - onTap: () => context.routeReplaceOrPush(PlayerSettingsRoute()), - ), - SettingsListTile( - label: Text(context.localized.about), - subLabel: const Text("Fladder"), - suffix: Opacity( - opacity: 1, - child: FladderIconOutlined( - size: 24, - color: context.colors.onSurfaceVariant, - )), - onTap: () => showAboutDialog( - context: context, - applicationIcon: const FladderIcon(size: 85), - applicationVersion: ref.watch(applicationInfoProvider).versionAndPlatform, - applicationLegalese: "Donut Factory", + if (quickConnectAvailable) + SettingsListTile( + label: Text(context.localized.settingsQuickConnectTitle), + icon: IconsaxOutline.password_check, + onTap: () => openQuickConnectDialog(context), + ), + SettingsListTile( + label: Text(context.localized.settingsProfileTitle), + subLabel: Text(context.localized.settingsProfileDesc), + selected: containsRoute(const SecuritySettingsRoute()), + icon: IconsaxOutline.security_user, + onTap: () => navigateTo(const SecuritySettingsRoute()), ), - ), - ], - floatingActionButton: HideOnScroll( - controller: scrollController, - visibleBuilder: (visible) { - return AnimatedFadeSize( - child: visible - ? Padding( - padding: EdgeInsets.symmetric(horizontal: MediaQuery.paddingOf(context).horizontal), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Spacer(), - FloatingActionButton( - key: Key(context.localized.switchUser), - tooltip: context.localized.switchUser, - onPressed: () { - ref.read(userProvider.notifier).logoutUser(); - context.routeGo(LoginRoute()); - }, - child: const Icon( - IconsaxOutline.arrow_swap_horizontal, + SettingsListTile( + label: Text(context.localized.settingsPlayerTitle), + subLabel: Text(context.localized.settingsPlayerDesc), + selected: containsRoute(const PlayerSettingsRoute()), + icon: IconsaxOutline.video_play, + onTap: () => navigateTo(const PlayerSettingsRoute()), + ), + SettingsListTile( + label: Text(context.localized.about), + subLabel: const Text("Fladder"), + suffix: Opacity( + opacity: 1, + child: FladderIconOutlined( + size: 24, + color: context.colors.onSurfaceVariant, + )), + onTap: () => showAboutDialog( + context: context, + applicationIcon: const FladderIcon(size: 85), + applicationVersion: ref.watch(applicationInfoProvider).versionAndPlatform, + applicationLegalese: "Donut Factory", + ), + ), + ], + floatingActionButton: HideOnScroll( + controller: scrollController, + visibleBuilder: (visible) { + return AnimatedFadeSize( + child: visible + ? Padding( + padding: EdgeInsets.symmetric(horizontal: MediaQuery.paddingOf(context).horizontal), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Spacer(), + FloatingActionButton( + key: Key(context.localized.switchUser), + tooltip: context.localized.switchUser, + onPressed: () async { + await ref.read(userProvider.notifier).logoutUser(); + context.router.navigate(const LoginRoute()); + }, + child: const Icon( + IconsaxOutline.arrow_swap_horizontal, + ), ), - ), - const SizedBox(width: 16), - FloatingActionButton( - key: Key(context.localized.logout), - tooltip: context.localized.logout, - backgroundColor: Theme.of(context).colorScheme.errorContainer, - onPressed: () { - final user = ref.read(userProvider); - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")), - scrollable: true, - content: Text( - context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""), - ), - actions: [ - ElevatedButton( - onPressed: () => Navigator.pop(context), - child: Text(context.localized.cancel), + const SizedBox(width: 16), + FloatingActionButton( + heroTag: context.localized.logout, + key: Key(context.localized.logout), + tooltip: context.localized.logout, + backgroundColor: Theme.of(context).colorScheme.errorContainer, + onPressed: () { + final user = ref.read(userProvider); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")), + scrollable: true, + content: Text( + context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""), ), - ElevatedButton( - style: ElevatedButton.styleFrom().copyWith( - foregroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer), - backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer), + actions: [ + ElevatedButton( + onPressed: () => Navigator.pop(context), + child: Text(context.localized.cancel), ), - onPressed: () async { - await ref.read(authProvider.notifier).logOutUser(); - if (context.mounted) context.routeGo(SplashRoute()); - }, - child: Text(context.localized.logout), - ), - ], - ), - ); - }, - child: Icon( - IconsaxOutline.logout, - color: Theme.of(context).colorScheme.onErrorContainer, + ElevatedButton( + style: ElevatedButton.styleFrom().copyWith( + foregroundColor: + WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer), + backgroundColor: + WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer), + ), + onPressed: () async { + await ref.read(authProvider.notifier).logOutUser(); + if (context.mounted) context.router.navigate(const LoginRoute()); + }, + child: Text(context.localized.logout), + ), + ], + ), + ); + }, + child: Icon( + IconsaxOutline.logout, + color: Theme.of(context).colorScheme.onErrorContainer, + ), ), - ), - ], + ], + ), ), + ) + : Container( + height: 0, + key: UniqueKey(), ), - ) - : Container( - height: 0, - key: UniqueKey(), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/screens/shared/detail_scaffold.dart b/lib/screens/shared/detail_scaffold.dart index 172a0e9..0f0e50b 100644 --- a/lib/screens/shared/detail_scaffold.dart +++ b/lib/screens/shared/detail_scaffold.dart @@ -1,22 +1,22 @@ -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/util/localization_helper.dart'; -import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart'; import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/images_models.dart'; import 'package:fladder/models/media_playback_model.dart'; import 'package:fladder/providers/items/item_details_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/theme.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/fladder_image.dart'; +import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/refresh_state.dart'; import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart'; +import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart'; import 'package:fladder/widgets/shared/item_actions.dart'; import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/pull_to_refresh.dart'; @@ -52,7 +52,7 @@ class _DetailScreenState extends ConsumerState { currentWidget = response.detailScreenWidget; }); } else { - context.routeGo(DashboardRoute()); + context.router.navigate(const DashboardRoute()); } } } @@ -212,13 +212,7 @@ class _DetailScaffoldState extends ConsumerState { style: IconButton.styleFrom( backgroundColor: backGroundColor, ), - onPressed: () { - if (context.canPop()) { - context.pop(); - } else { - context.replace(DashboardRoute().route); - } - }, + onPressed: () => context.router.maybePop(), icon: Padding( padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4), @@ -281,7 +275,7 @@ class _DetailScaffoldState extends ConsumerState { Tooltip( message: context.localized.home, child: IconButton( - onPressed: () => context.routeGo(DashboardRoute()), + onPressed: () => context.router.navigate(const DashboardRoute()), icon: const Icon(IconsaxOutline.home), ), ), diff --git a/lib/screens/shared/floating_search_bar.dart b/lib/screens/shared/floating_search_bar.dart index 34fe79f..f4fe13d 100644 --- a/lib/screens/shared/floating_search_bar.dart +++ b/lib/screens/shared/floating_search_bar.dart @@ -1,13 +1,12 @@ import 'package:animations/animations.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/search/search_screen.dart'; import 'package:fladder/util/string_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; class FloatingSearchBar extends ConsumerStatefulWidget { final List trailing; @@ -58,9 +57,9 @@ class _FloatingSearchBarState extends ConsumerState { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (context.canPop()) + if (context.router.canPop()) IconButton( - onPressed: () => context.pop(), + onPressed: () => context.router.maybePop(), icon: const Icon(Icons.arrow_back), ), const SizedBox(width: 8), @@ -77,7 +76,7 @@ class _FloatingSearchBarState extends ConsumerState { ), IconButton( onPressed: () { - context.routeGo(SecuritySettingsRoute()); + context.router.push(const SecuritySettingsRoute()); }, icon: ClipRRect( borderRadius: BorderRadius.circular(200), diff --git a/lib/screens/shared/nested_sliver_appbar.dart b/lib/screens/shared/nested_sliver_appbar.dart index ad379b4..93d20d8 100644 --- a/lib/screens/shared/nested_sliver_appbar.dart +++ b/lib/screens/shared/nested_sliver_appbar.dart @@ -1,5 +1,5 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart'; @@ -10,7 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class NestedSliverAppBar extends ConsumerWidget { final BuildContext parent; final String? searchTitle; - final CustomRoute? route; + final PageRouteInfo? route; const NestedSliverAppBar({required this.parent, this.route, this.searchTitle, super.key}); @override @@ -49,7 +49,7 @@ class NestedSliverAppBar extends ConsumerWidget { child: InkWell( onTap: route != null ? () { - context.routePushOrGo(route!); + route?.push(context); } : null, child: Padding( @@ -62,7 +62,8 @@ class NestedSliverAppBar extends ConsumerWidget { const Icon(IconsaxOutline.search_normal), const SizedBox(width: 16), Transform.translate( - offset: const Offset(0, 2.5), child: Text(searchTitle ?? "${context.localized.search}...")), + offset: const Offset(0, 2.5), + child: Text(searchTitle ?? "${context.localized.search}...")), ], ), ), diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index af7bbcb..0849160 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -1,14 +1,17 @@ +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/account_model.dart'; import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; import 'package:fladder/screens/shared/fladder_logo.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +@RoutePage() class SplashScreen extends ConsumerStatefulWidget { - const SplashScreen({super.key}); + final Function(bool loggedIn)? loggedIn; + const SplashScreen({this.loggedIn, super.key}); @override ConsumerState createState() => _SplashScreenState(); @@ -18,23 +21,26 @@ class _SplashScreenState extends ConsumerState { @override void initState() { super.initState(); - Future.microtask(() async { + WidgetsBinding.instance.addPostFrameCallback((value) async { await Future.delayed(const Duration(milliseconds: 500)); final AccountModel? lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount(); ref.read(userProvider.notifier).updateUser(lastUsedAccount); if (context.mounted) { if (lastUsedAccount == null) { - context.routeGo(LoginRoute()); + widget.loggedIn?.call(false); + context.router.maybePop(false); } else { switch (lastUsedAccount.authMethod) { case Authentication.autoLogin: - context.routeGo(DashboardRoute()); + widget.loggedIn?.call(true); + context.router.maybePop(true); break; case Authentication.biometrics: case Authentication.none: case Authentication.passcode: - context.routeReplace(LoginRoute()); + widget.loggedIn?.call(false); + context.router.maybePop(false); break; } } diff --git a/lib/screens/syncing/synced_screen.dart b/lib/screens/syncing/synced_screen.dart index 97d324a..7ae8d12 100644 --- a/lib/screens/syncing/synced_screen.dart +++ b/lib/screens/syncing/synced_screen.dart @@ -1,7 +1,8 @@ +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:fladder/providers/sync_provider.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/nested_scaffold.dart'; import 'package:fladder/screens/shared/nested_sliver_appbar.dart'; import 'package:fladder/screens/syncing/sync_list_item.dart'; @@ -14,10 +15,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/util/sliver_list_padding.dart'; +@RoutePage() class SyncedScreen extends ConsumerStatefulWidget { - final ScrollController navigationScrollController; + final ScrollController? navigationScrollController; - const SyncedScreen({required this.navigationScrollController, super.key}); + const SyncedScreen({this.navigationScrollController, super.key}); @override ConsumerState createState() => _SyncedScreenState(); diff --git a/lib/util/adaptive_layout.dart b/lib/util/adaptive_layout.dart index ae0fe01..d0a9fbb 100644 --- a/lib/util/adaptive_layout.dart +++ b/lib/util/adaptive_layout.dart @@ -1,11 +1,10 @@ -import 'package:fladder/util/poster_defaults.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:fladder/routes/app_routes.dart'; +import 'package:fladder/routes/auto_router.dart'; +import 'package:fladder/util/poster_defaults.dart'; enum LayoutState { phone, @@ -65,8 +64,9 @@ class AdaptiveLayout extends InheritedWidget { final InputDevice inputDevice; final TargetPlatform platform; final bool isDesktop; - final GoRouter router; + final AutoRouter router; final PosterDefaults posterDefaults; + final ScrollController controller; const AdaptiveLayout({ super.key, @@ -77,6 +77,7 @@ class AdaptiveLayout extends InheritedWidget { required this.isDesktop, required this.router, required this.posterDefaults, + required this.controller, required super.child, }); @@ -94,7 +95,7 @@ class AdaptiveLayout extends InheritedWidget { return result!.posterDefaults; } - static GoRouter routerOf(BuildContext context) { + static AutoRouter routerOf(BuildContext context) { final AdaptiveLayout? result = maybeOf(context); return result!.router; } @@ -104,6 +105,11 @@ class AdaptiveLayout extends InheritedWidget { return result!; } + static ScrollController scrollOf(BuildContext context) { + final AdaptiveLayout? result = maybeOf(context); + return result!.controller; + } + @override bool updateShouldNotify(AdaptiveLayout oldWidget) { return layout != oldWidget.layout || @@ -128,8 +134,9 @@ class AdaptiveLayoutBuilder extends ConsumerStatefulWidget { class _AdaptiveLayoutBuilderState extends ConsumerState { late LayoutState layout = widget.fallBack; late ScreenLayout size = ScreenLayout.single; - late GoRouter router = AppRoutes.routes(ref: ref, screenLayout: size); + late AutoRouter router = AutoRouter(layout: size, ref: ref); late TargetPlatform currentPlatform = defaultTargetPlatform; + late ScrollController controller = ScrollController(); @override void didChangeDependencies() { @@ -169,7 +176,7 @@ class _AdaptiveLayoutBuilderState extends ConsumerState { } if (size != newSize) { size = newSize; - router = AppRoutes.routes(ref: ref, screenLayout: size); + router = AutoRouter(layout: size, ref: ref); } } @@ -177,6 +184,7 @@ class _AdaptiveLayoutBuilderState extends ConsumerState { Widget build(BuildContext context) { return AdaptiveLayout( layout: layout, + controller: controller, size: size, inputDevice: (isDesktop || kIsWeb) ? InputDevice.pointer : InputDevice.touch, platform: currentPlatform, @@ -191,3 +199,10 @@ class _AdaptiveLayoutBuilderState extends ConsumerState { ); } } + +double? get topPadding { + return switch (defaultTargetPlatform) { + TargetPlatform.linux || TargetPlatform.windows || TargetPlatform.macOS => 35, + _ => null + }; +} diff --git a/lib/widgets/navigation_scaffold/components/destination_model.dart b/lib/widgets/navigation_scaffold/components/destination_model.dart index 6ad6b40..2761745 100644 --- a/lib/widgets/navigation_scaffold/components/destination_model.dart +++ b/lib/widgets/navigation_scaffold/components/destination_model.dart @@ -1,4 +1,4 @@ -import 'package:fladder/routes/build_routes/route_builder.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart'; import 'package:fladder/widgets/navigation_scaffold/components/navigation_button.dart'; import 'package:flutter/material.dart'; @@ -7,7 +7,7 @@ class DestinationModel { final String label; final Widget? icon; final Widget? selectedIcon; - final CustomRoute? route; + final PageRouteInfo? route; final Function()? action; final String? tooltip; final Badge? badge; diff --git a/lib/widgets/navigation_scaffold/components/fladder_appbar.dart b/lib/widgets/navigation_scaffold/components/fladder_appbar.dart index 994eefe..44d8cdc 100644 --- a/lib/widgets/navigation_scaffold/components/fladder_appbar.dart +++ b/lib/widgets/navigation_scaffold/components/fladder_appbar.dart @@ -1,9 +1,9 @@ +import 'package:auto_route/auto_route.dart'; import 'package:fladder/screens/shared/default_titlebar.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:go_router/go_router.dart'; bool get _isDesktop { if (kIsWeb) return false; @@ -29,7 +29,7 @@ class FladderAppbar extends StatelessWidget implements PreferredSize { height: height, child: Row( children: [ - if (automaticallyImplyLeading && context.canPop()) const BackButton(), + if (automaticallyImplyLeading && context.router.canPop()) const BackButton(), Expanded( child: DefaultTitleBar( label: label, diff --git a/lib/widgets/navigation_scaffold/components/navigation_body.dart b/lib/widgets/navigation_scaffold/components/navigation_body.dart index 801fde4..479cbb7 100644 --- a/lib/widgets/navigation_scaffold/components/navigation_body.dart +++ b/lib/widgets/navigation_scaffold/components/navigation_body.dart @@ -5,7 +5,7 @@ import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/providers/views_provider.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/animated_fade_size.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart'; @@ -110,7 +110,10 @@ class _NavigationBodyState extends ConsumerState { style: Theme.of(context).textTheme.titleSmall, ), }, - if (AdaptiveLayout.of(context).platform == TargetPlatform.macOS) const SizedBox(height: 32) else const SizedBox(height: 16), + if (AdaptiveLayout.of(context).platform == TargetPlatform.macOS) + const SizedBox(height: 32) + else + const SizedBox(height: 16), IconButton( onPressed: () { if (AdaptiveLayout.layoutOf(context) != LayoutState.desktop) { @@ -152,7 +155,7 @@ class _NavigationBodyState extends ConsumerState { height: 48, child: AnimatedSwitcher( duration: const Duration(milliseconds: 250), - child: widget.currentLocation.contains(SettingsRoute().route) + child: widget.currentLocation.contains(const SettingsRoute().routeName) ? Card( color: Theme.of(context).colorScheme.primaryContainer, child: const Padding( diff --git a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart index 915331b..ac908fd 100644 --- a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart +++ b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart @@ -1,14 +1,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/models/collection_types.dart'; import 'package:fladder/models/view_model.dart'; -import 'package:fladder/routes/build_routes/home_routes.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/metadata/refresh_metadata.dart'; import 'package:fladder/screens/shared/animated_fade_size.dart'; import 'package:fladder/util/adaptive_layout.dart'; @@ -74,7 +73,7 @@ class NestedNavigationDrawer extends ConsumerWidget { ), ...destinations.map((destination) => DrawerListButton( label: destination.label, - selected: destination.route?.route == currentLocation, + selected: context.router.current.name == destination.route?.routeName, selectedIcon: destination.selectedIcon!, icon: destination.icon!, onPressed: () { @@ -93,7 +92,8 @@ class NestedNavigationDrawer extends ConsumerWidget { ), ...views.map((library) => DrawerListButton( label: library.name, - selected: currentLocation.contains(library.id), + selected: context.router.current.name == LibrarySearchRoute().routeName && + context.routeData.queryParams.getString('parentId') == library.id, actions: [ ItemActionButton( label: Text(context.localized.scanLibrary), @@ -102,7 +102,7 @@ class NestedNavigationDrawer extends ConsumerWidget { ), ], onPressed: () { - context.routePushOrGo(LibrarySearchRoute(id: library.id)); + context.router.push(LibrarySearchRoute(viewModelId: library.id)); Scaffold.of(context).closeDrawer(); }, selectedIcon: Icon(library.collectionType.icon), @@ -115,15 +115,15 @@ class NestedNavigationDrawer extends ConsumerWidget { child: DrawerListButton( label: context.localized.settings, selectedIcon: const Icon(IconsaxBold.setting_3), - selected: currentLocation.contains(SettingsRoute().basePath), + selected: currentLocation.contains(const SettingsRoute().routeName), icon: const SizedBox(width: 35, height: 35, child: SettingsUserIcon()), onPressed: () { switch (AdaptiveLayout.of(context).size) { case ScreenLayout.single: - context.routePush(SettingsRoute()); + const SettingsRoute().push(context); break; case ScreenLayout.dual: - context.routeGo(ClientSettingsRoute()); + context.router.push(const ClientSettingsRoute()); break; } Scaffold.of(context).closeDrawer(); @@ -135,14 +135,14 @@ class NestedNavigationDrawer extends ConsumerWidget { label: context.localized.settings, selectedIcon: const Icon(IconsaxBold.setting_2), icon: const Icon(IconsaxOutline.setting_2), - selected: currentLocation.contains(SettingsRoute().basePath), + selected: currentLocation.contains(const SettingsRoute().routeName), onPressed: () { switch (AdaptiveLayout.of(context).size) { case ScreenLayout.single: - context.routePush(SettingsRoute()); + const SettingsRoute().push(context); break; case ScreenLayout.dual: - context.routeGo(ClientSettingsRoute()); + context.router.push(const ClientSettingsRoute()); break; } Scaffold.of(context).closeDrawer(); diff --git a/lib/widgets/navigation_scaffold/components/settings_user_icon.dart b/lib/widgets/navigation_scaffold/components/settings_user_icon.dart index b09ee2d..f6b5931 100644 --- a/lib/widgets/navigation_scaffold/components/settings_user_icon.dart +++ b/lib/widgets/navigation_scaffold/components/settings_user_icon.dart @@ -1,8 +1,7 @@ +import 'package:auto_route/auto_route.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/routes/build_routes/route_builder.dart'; -import 'package:fladder/routes/build_routes/settings_routes.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/user_icon.dart'; -import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -19,11 +18,8 @@ class SettingsUserIcon extends ConsumerWidget { child: UserIcon( user: users, cornerRadius: 200, - onLongPress: () => context.routePush(LockScreenRoute()), - onTap: () => switch (AdaptiveLayout.of(context).size) { - ScreenLayout.single => context.routePush(SettingsRoute()), - ScreenLayout.dual => context.routePush(ClientSettingsRoute()), - }, + onLongPress: () => context.router.push(const LockRoute()), + onTap: () => context.router.navigate(const SettingsRoute()), ), ); } diff --git a/lib/widgets/navigation_scaffold/navigation_scaffold.dart b/lib/widgets/navigation_scaffold/navigation_scaffold.dart index b5226ce..3d7b960 100644 --- a/lib/widgets/navigation_scaffold/navigation_scaffold.dart +++ b/lib/widgets/navigation_scaffold/navigation_scaffold.dart @@ -1,12 +1,11 @@ import 'package:fladder/models/media_playback_model.dart'; import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/providers/views_provider.dart'; -import 'package:fladder/routes/app_routes.dart'; import 'package:fladder/screens/shared/nested_bottom_appbar.dart'; import 'package:fladder/util/adaptive_layout.dart'; -import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart'; import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart'; import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart'; +import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart'; import 'package:fladder/widgets/navigation_scaffold/components/navigation_body.dart'; import 'package:fladder/widgets/navigation_scaffold/components/navigation_drawer.dart'; import 'package:fladder/widgets/shared/hide_on_scroll.dart'; @@ -14,14 +13,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class NavigationScaffold extends ConsumerStatefulWidget { - final int? currentIndex; - final String? location; + final String? currentRouteName; final Widget? nestedChild; final List destinations; final GlobalKey? nestedNavigatorKey; const NavigationScaffold({ - this.currentIndex, - this.location, + this.currentRouteName, this.nestedChild, required this.destinations, this.nestedNavigatorKey, @@ -35,8 +32,9 @@ class NavigationScaffold extends ConsumerStatefulWidget { class _NavigationScaffoldState extends ConsumerState { final GlobalKey _key = GlobalKey(); - int get currentIndex => widget.destinations.indexWhere((element) => element.route?.route == widget.location); - String get currentLocation => widget.location ?? "Nothing"; + int get currentIndex => + widget.destinations.indexWhere((element) => element.route?.routeName == widget.currentRouteName); + String get currentLocation => widget.currentRouteName ?? "Nothing"; @override void initState() { @@ -50,6 +48,7 @@ class _NavigationScaffoldState extends ConsumerState { Widget build(BuildContext context) { final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state)); final views = ref.watch(viewsProvider.select((value) => value.views)); + return PopScope( canPop: currentIndex == 0, onPopInvokedWithResult: (didPop, result) { @@ -70,21 +69,21 @@ class _NavigationScaffoldState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: 8), child: FloatingPlayerBar(), ), - _ => widget.destinations.elementAtOrNull(currentIndex)?.floatingActionButton?.normal, + _ => currentIndex != -1 + ? widget.destinations.elementAtOrNull(currentIndex)?.floatingActionButton?.normal + : null, } : null, drawer: NestedNavigationDrawer( actionButton: null, - toggleExpanded: (value) { - _key.currentState?.closeDrawer(); - }, + toggleExpanded: (value) => _key.currentState?.closeDrawer(), views: views, destinations: widget.destinations, currentLocation: currentLocation, ), bottomNavigationBar: AdaptiveLayout.of(context).layout == LayoutState.phone ? HideOnScroll( - controller: AppRoutes.scrollController, + controller: AdaptiveLayout.scrollOf(context), child: NestedBottomAppBar( child: Transform.translate( offset: const Offset(0, 8), @@ -93,8 +92,8 @@ class _NavigationScaffoldState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: widget.destinations .map( - (destination) => - destination.toNavigationButton(widget.location == destination.route?.route, false), + (destination) => destination.toNavigationButton( + widget.currentRouteName == destination.route?.routeName, false), ) .toList(), ), diff --git a/lib/wrappers/media_control_wrapper.dart b/lib/wrappers/media_control_wrapper.dart index bc8bde2..34df533 100644 --- a/lib/wrappers/media_control_wrapper.dart +++ b/lib/wrappers/media_control_wrapper.dart @@ -1,20 +1,22 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; + import 'package:audio_service/audio_service.dart'; import 'package:collection/collection.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_kit_video/media_kit_video.dart'; +import 'package:smtc_windows/smtc_windows.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/video_player_settings_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/wrappers/media_control_base.dart'; import 'package:fladder/wrappers/media_wrapper_interface.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:media_kit/media_kit.dart'; -import 'package:media_kit_video/media_kit_video.dart'; -import 'package:smtc_windows/smtc_windows.dart'; -import 'package:wakelock_plus/wakelock_plus.dart'; class MediaControlsWrapper extends MediaPlayback implements MediaControlBase { MediaControlsWrapper({required this.ref}); diff --git a/pubspec.lock b/pubspec.lock index 9635337..e043262 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,31 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.7.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" url: "https://pub.dev" source: hosted - version: "0.11.2" + version: "0.11.3" animations: dependency: "direct main" description: @@ -97,6 +102,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.19" + auto_route: + dependency: "direct main" + description: + name: auto_route + sha256: b83e8ce46da7228cdd019b5a11205454847f0a971bca59a7529b98df9876889b + url: "https://pub.dev" + source: hosted + version: "9.2.2" + auto_route_generator: + dependency: "direct dev" + description: + name: auto_route_generator + sha256: c9086eb07271e51b44071ad5cff34e889f3156710b964a308c2ab590769e79e6 + url: "https://pub.dev" + source: hosted + version: "9.0.0" automatic_animated_list: dependency: "direct main" description: @@ -317,10 +338,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" url: "https://pub.dev" source: hosted - version: "0.3.4+1" + version: "0.3.4+2" crypto: dependency: transitive description: @@ -381,10 +402,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.7" dbus: dependency: transitive description: @@ -445,10 +466,10 @@ packages: dependency: transitive description: name: extended_image_library - sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6 + sha256: "9a94ec9314aa206cfa35f16145c3cd6e2c924badcc670eaaca8a3a8063a68cd7" url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.0.5" fake_async: dependency: transitive description: @@ -773,14 +794,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: cdae1b9c8bd7efadcef6112e81c903662ef2ce105cbd220a04bbb7c3425b5554 - url: "https://pub.dev" - source: hosted - version: "14.2.0" google_fonts: dependency: "direct main" description: @@ -929,10 +942,10 @@ packages: dependency: transitive description: name: just_audio_web - sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c" + sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448" url: "https://pub.dev" source: hosted - version: "0.4.11" + version: "0.4.13" leak_tracker: dependency: transitive description: @@ -1013,6 +1026,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" markdown: dependency: transitive description: @@ -1161,18 +1182,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 + sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "8.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e + sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" page_transition: dependency: "direct main" description: @@ -1345,10 +1366,10 @@ packages: dependency: transitive description: name: pointer_interceptor_web - sha256: a6237528b46c411d8d55cdfad8fcb3269fc4cbb26060b14bff94879165887d1e + sha256: "7a7087782110f8c1827170660b09f8aa893e0e9a61431dbbe2ac3fc482e8c044" url: "https://pub.dev" source: hosted - version: "0.10.2" + version: "0.10.2+1" pool: dependency: transitive description: @@ -1593,10 +1614,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: @@ -1910,10 +1931,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -2030,10 +2051,10 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: "14758533319a462ffb5aa3b7ddb198e59b29ac3b02da14173a1715d65d4e6e68" + sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484 url: "https://pub.dev" source: hosted - version: "1.2.5" + version: "1.2.8" wakelock_plus_platform_interface: dependency: transitive description: @@ -2062,18 +2083,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.0" webview_flutter: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 64bb9ca..f47faee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,7 +91,7 @@ dependencies: flutter_widget_from_html: ^0.14.11 # Navigation - go_router: ^14.2.0 + auto_route: ^9.2.2 url_launcher: ^6.1.10 flutter_custom_tabs: ^1.0.4 @@ -147,6 +147,7 @@ dev_dependencies: swagger_dart_code_generator: ^2.15.2 riverpod_generator: ^2.4.0 dart_mappable_builder: ^4.2.3 + auto_route_generator: ^9.0.0 flutter: # The following line ensures that the Material Icons font is From 0c60501a997f0a34a56cf3ccbcf1a0d7f9211ac3 Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sat, 5 Oct 2024 17:52:33 +0200 Subject: [PATCH 2/5] [Fix] Move web to os name --- lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 8573abc..b79018f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -83,9 +83,9 @@ void main() async { } final applicationInfo = ApplicationInfo( - name: kIsWeb ? "${packageInfo.appName.capitalize()} Web" : packageInfo.appName.capitalize(), + name: packageInfo.appName.capitalize(), version: "${packageInfo.version}(${packageInfo.buildNumber})", - os: defaultTargetPlatform.name.capitalize(), + os: !kIsWeb ? defaultTargetPlatform.name.capitalize() : "${defaultTargetPlatform.name.capitalize()} Web", ); runApp( From 249bd685b190b4023e6ee1fc89f3b927733143a0 Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sat, 5 Oct 2024 19:15:31 +0200 Subject: [PATCH 3/5] Fixed dashboard navigation --- lib/screens/login/login_screen.dart | 2 +- lib/screens/settings/settings_scaffold.dart | 2 +- lib/screens/settings/settings_screen.dart | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/screens/login/login_screen.dart b/lib/screens/login/login_screen.dart index 16a2fc4..181898f 100644 --- a/lib/screens/login/login_screen.dart +++ b/lib/screens/login/login_screen.dart @@ -211,7 +211,7 @@ class _LoginPageState extends ConsumerState { void loggedInGoToHome() { ref.read(lockScreenActiveProvider.notifier).update((state) => false); if (context.mounted) { - context.router.push(const DashboardRoute()); + context.router.navigate(const DashboardRoute()); } } diff --git a/lib/screens/settings/settings_scaffold.dart b/lib/screens/settings/settings_scaffold.dart index 4223bc0..f6b37df 100644 --- a/lib/screens/settings/settings_scaffold.dart +++ b/lib/screens/settings/settings_scaffold.dart @@ -27,7 +27,7 @@ class SettingsScaffold extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final padding = MediaQuery.of(context).padding; return Scaffold( - backgroundColor: AdaptiveLayout.of(context).isDesktop ? Colors.transparent : null, + backgroundColor: AdaptiveLayout.of(context).size == ScreenLayout.dual ? Colors.transparent : null, floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: floatingActionButton, body: Column( diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 11675b8..1dfb0e5 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -90,7 +90,7 @@ class _SettingsScreenState extends ConsumerState { scrollController: scrollController, showUserIcon: true, items: [ - if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.single) + if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.dual) Align( alignment: Alignment.centerLeft, child: Padding( @@ -205,7 +205,9 @@ class _SettingsScreenState extends ConsumerState { ), onPressed: () async { await ref.read(authProvider.notifier).logOutUser(); - if (context.mounted) context.router.navigate(const LoginRoute()); + if (context.mounted) { + context.router.navigate(const LoginRoute()); + } }, child: Text(context.localized.logout), ), From 69b3a77d17648defee82e0b517e2d7cd5edbfcde Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sat, 5 Oct 2024 19:52:05 +0200 Subject: [PATCH 4/5] Fix for empty queryParameters --- .../components/navigation_drawer.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart index ac908fd..a2f47c5 100644 --- a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart +++ b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart @@ -92,8 +92,7 @@ class NestedNavigationDrawer extends ConsumerWidget { ), ...views.map((library) => DrawerListButton( label: library.name, - selected: context.router.current.name == LibrarySearchRoute().routeName && - context.routeData.queryParams.getString('parentId') == library.id, + selected: checkLibrary(context, library.id), actions: [ ItemActionButton( label: Text(context.localized.scanLibrary), @@ -152,4 +151,12 @@ class NestedNavigationDrawer extends ConsumerWidget { ], ); } + + bool checkLibrary(BuildContext context, String id) { + try { + return context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id; + } catch (e) { + return false; + } + } } From d19fa0ccd0fd44d0580252faeae6dc303a248fbb Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sun, 6 Oct 2024 19:01:46 +0200 Subject: [PATCH 5/5] [Fixed] Use browser navigation --- lib/providers/library_search_provider.dart | 25 ++++--- lib/routes/auto_router.dart | 13 ++-- lib/routes/nested_details_screen.dart | 22 +++++- .../details_screens/book_detail_screen.dart | 12 ++-- .../episode_detail_screen.dart | 20 +++--- .../details_screens/movie_detail_screen.dart | 16 +++-- .../details_screens/season_detail_screen.dart | 10 ++- .../details_screens/series_detail_screen.dart | 23 +++--- .../library_search/library_search_screen.dart | 46 ++++++++---- .../widgets/library_filter_chips.dart | 10 +-- lib/screens/login/login_screen.dart | 2 +- lib/screens/metadata/identifty_screen.dart | 8 ++- lib/screens/metadata/info_screen.dart | 10 +-- .../settings/client_settings_page.dart | 6 +- lib/screens/settings/settings_scaffold.dart | 3 + lib/screens/settings/settings_screen.dart | 5 +- lib/screens/shared/detail_scaffold.dart | 70 ++----------------- lib/screens/splash_screen.dart | 23 ++++-- lib/util/router_extension.dart | 30 ++++++++ .../components/navigation_drawer.dart | 3 +- .../navigation_scaffold.dart | 6 +- lib/widgets/shared/trickplay_image.dart | 3 +- 22 files changed, 210 insertions(+), 156 deletions(-) create mode 100644 lib/util/router_extension.dart diff --git a/lib/providers/library_search_provider.dart b/lib/providers/library_search_provider.dart index 5130073..764f08d 100644 --- a/lib/providers/library_search_provider.dart +++ b/lib/providers/library_search_provider.dart @@ -1,31 +1,32 @@ import 'dart:developer'; +import 'package:flutter/material.dart'; + import 'package:chopper/chopper.dart'; import 'package:collection/collection.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:page_transition/page_transition.dart'; + +import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/models/collection_types.dart'; +import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/folder_model.dart'; import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/items/photos_model.dart'; +import 'package:fladder/models/library_search/library_search_model.dart'; import 'package:fladder/models/library_search/library_search_options.dart'; import 'package:fladder/models/playlist_model.dart'; +import 'package:fladder/models/view_model.dart'; +import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/service_provider.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; +import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/photo_viewer/photo_viewer_screen.dart'; import 'package:fladder/screens/shared/fladder_snackbar.dart'; import 'package:fladder/util/item_base_model/play_item_helpers.dart'; import 'package:fladder/util/list_extensions.dart'; import 'package:fladder/util/localization_helper.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; -import 'package:fladder/models/item_base_model.dart'; -import 'package:fladder/models/library_search/library_search_model.dart'; -import 'package:fladder/models/view_model.dart'; -import 'package:fladder/providers/api_provider.dart'; -import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/util/map_bool_helper.dart'; -import 'package:page_transition/page_transition.dart'; final librarySearchProvider = StateNotifierProvider.family.autoDispose((ref, id) { @@ -652,6 +653,10 @@ class LibrarySearchNotifier extends StateNotifier { items.firstOrNull?.navigateTo(context); } } + + void updateEverything() { + state = state.copyWith(); + } } extension SimpleSorter on List { diff --git a/lib/routes/auto_router.dart b/lib/routes/auto_router.dart index 9770c28..ad0dc76 100644 --- a/lib/routes/auto_router.dart +++ b/lib/routes/auto_router.dart @@ -10,7 +10,10 @@ import 'package:fladder/util/adaptive_layout.dart'; @AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route') class AutoRouter extends RootStackRouter { - AutoRouter({required this.layout, required this.ref}); + AutoRouter({ + required this.layout, + required this.ref, + }); final WidgetRef ref; final ScreenLayout layout; @@ -35,8 +38,8 @@ class AutoRouter extends RootStackRouter { _syncedRoute, ], ), - AutoRoute(page: DetailsRoute.page, path: '/details'), - AutoRoute(page: LibrarySearchRoute.page, path: '/library'), + AutoRoute(page: DetailsRoute.page, path: '/details', usesPathAsKey: true), + AutoRoute(page: LibrarySearchRoute.page, path: '/library', usesPathAsKey: true), AutoRoute(page: SettingsRoute.page, path: '/settings'), ..._settingsChildren.map( (e) => e.copyWith(path: "/$e", initial: false), @@ -49,8 +52,8 @@ class AutoRouter extends RootStackRouter { _dashboardRoute, _favouritesRoute, _syncedRoute, - AutoRoute(page: DetailsRoute.page, path: 'details'), - AutoRoute(page: LibrarySearchRoute.page, path: 'library'), + AutoRoute(page: DetailsRoute.page, path: 'details', usesPathAsKey: true), + AutoRoute(page: LibrarySearchRoute.page, path: 'library', usesPathAsKey: true), AutoRoute( page: SettingsRoute.page, path: 'settings', diff --git a/lib/routes/nested_details_screen.dart b/lib/routes/nested_details_screen.dart index 8e95c99..e545855 100644 --- a/lib/routes/nested_details_screen.dart +++ b/lib/routes/nested_details_screen.dart @@ -1,10 +1,13 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + import 'package:auto_route/auto_route.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/item_details_provider.dart'; import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/util/fladder_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; @RoutePage() class DetailsScreen extends ConsumerStatefulWidget { @@ -22,9 +25,21 @@ class _DetailsScreenState extends ConsumerState { child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round), ); + @override + void didUpdateWidget(covariant DetailsScreen oldWidget) { + super.didUpdateWidget(oldWidget); + if (kIsWeb) { + updateWidget(); + } + } + @override void initState() { super.initState(); + updateWidget(); + } + + Future updateWidget() async { Future.microtask(() async { if (widget.item != null) { setState(() { @@ -38,7 +53,7 @@ class _DetailsScreenState extends ConsumerState { currentWidget = response.detailScreenWidget; }); } else { - const DashboardRoute().navigate(context); + const DashboardRoute().navigate(context); } } } @@ -48,6 +63,7 @@ class _DetailsScreenState extends ConsumerState { @override Widget build(BuildContext context) { return Stack( + key: Key(widget.id), children: [ Hero( tag: widget.id, diff --git a/lib/screens/details_screens/book_detail_screen.dart b/lib/screens/details_screens/book_detail_screen.dart index b77f703..2288566 100644 --- a/lib/screens/details_screens/book_detail_screen.dart +++ b/lib/screens/details_screens/book_detail_screen.dart @@ -1,5 +1,9 @@ +import 'package:flutter/material.dart'; + import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/book_model.dart'; import 'package:fladder/providers/items/book_details_provider.dart'; import 'package:fladder/providers/user_provider.dart'; @@ -13,12 +17,11 @@ import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; import 'package:fladder/util/item_base_model/play_item_helpers.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/localization_helper.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/item_actions.dart'; import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class BookDetailScreen extends ConsumerStatefulWidget { final BookModel item; @@ -29,7 +32,8 @@ class BookDetailScreen extends ConsumerStatefulWidget { } class _BookDetailScreenState extends ConsumerState { - late final provider = bookDetailsProvider(widget.item.id); + AutoDisposeStateNotifierProvider get provider => + bookDetailsProvider(widget.item.id); @override Widget build(BuildContext context) { @@ -47,7 +51,7 @@ class _BookDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.router.maybePop(); + context.router.popBack(); } }, ), diff --git a/lib/screens/details_screens/episode_detail_screen.dart b/lib/screens/details_screens/episode_detail_screen.dart index 9b88e6e..17d0792 100644 --- a/lib/screens/details_screens/episode_detail_screen.dart +++ b/lib/screens/details_screens/episode_detail_screen.dart @@ -1,25 +1,26 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/screens/details_screens/components/overview_header.dart'; -import 'package:fladder/screens/shared/media/components/media_play_button.dart'; -import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; -import 'package:fladder/util/item_base_model/play_item_helpers.dart'; -import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/episode_details_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/details_screens/components/media_stream_information.dart'; +import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/shared/detail_scaffold.dart'; import 'package:fladder/screens/shared/fladder_snackbar.dart'; import 'package:fladder/screens/shared/media/chapter_row.dart'; import 'package:fladder/screens/shared/media/components/media_header.dart'; +import 'package:fladder/screens/shared/media/components/media_play_button.dart'; import 'package:fladder/screens/shared/media/episode_posters.dart'; import 'package:fladder/screens/shared/media/expanding_overview.dart'; +import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; +import 'package:fladder/util/item_base_model/play_item_helpers.dart'; import 'package:fladder/util/list_padding.dart'; +import 'package:fladder/util/localization_helper.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; @@ -32,7 +33,8 @@ class EpisodeDetailScreen extends ConsumerStatefulWidget { } class _ItemDetailScreenState extends ConsumerState { - late final providerInstance = episodeDetailsProvider(widget.item.id); + AutoDisposeStateNotifierProvider get providerInstance => + episodeDetailsProvider(widget.item.id); @override Widget build(BuildContext context) { @@ -52,7 +54,7 @@ class _ItemDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.router.maybePop(); + context.router.popBack(); } }, ), diff --git a/lib/screens/details_screens/movie_detail_screen.dart b/lib/screens/details_screens/movie_detail_screen.dart index a235d78..bb87805 100644 --- a/lib/screens/details_screens/movie_detail_screen.dart +++ b/lib/screens/details_screens/movie_detail_screen.dart @@ -1,25 +1,27 @@ +import 'package:fladder/util/router_extension.dart'; +import 'package:flutter/material.dart'; + import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/movies_details_provider.dart'; import 'package:fladder/providers/user_provider.dart'; -import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/details_screens/components/media_stream_information.dart'; -import 'package:fladder/screens/shared/media/components/media_header.dart'; +import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/shared/detail_scaffold.dart'; import 'package:fladder/screens/shared/media/chapter_row.dart'; +import 'package:fladder/screens/shared/media/components/media_header.dart'; import 'package:fladder/screens/shared/media/components/media_play_button.dart'; import 'package:fladder/screens/shared/media/expanding_overview.dart'; import 'package:fladder/screens/shared/media/people_row.dart'; import 'package:fladder/screens/shared/media/poster_row.dart'; import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; import 'package:fladder/util/item_base_model/play_item_helpers.dart'; - import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class MovieDetailScreen extends ConsumerStatefulWidget { final ItemBaseModel item; @@ -30,7 +32,7 @@ class MovieDetailScreen extends ConsumerStatefulWidget { } class _ItemDetailScreenState extends ConsumerState { - late final providerInstance = movieDetailsProvider(widget.item.id); + MovieDetailsProvider get providerInstance => movieDetailsProvider(widget.item.id); @override Widget build(BuildContext context) { @@ -49,7 +51,7 @@ class _ItemDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.router.maybePop(); + context.router.popBack(); } }, ), diff --git a/lib/screens/details_screens/season_detail_screen.dart b/lib/screens/details_screens/season_detail_screen.dart index e34be27..ec2874f 100644 --- a/lib/screens/details_screens/season_detail_screen.dart +++ b/lib/screens/details_screens/season_detail_screen.dart @@ -1,5 +1,10 @@ +import 'package:flutter/material.dart'; + import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/item_base_model.dart'; +import 'package:fladder/models/items/season_model.dart'; import 'package:fladder/providers/items/season_details_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/details_screens/components/overview_header.dart'; @@ -16,8 +21,6 @@ import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/string_extensions.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class SeasonDetailScreen extends ConsumerStatefulWidget { final ItemBaseModel item; @@ -29,7 +32,8 @@ class SeasonDetailScreen extends ConsumerStatefulWidget { class _SeasonDetailScreenState extends ConsumerState { Set viewOptions = {EpisodeDetailsViewType.grid}; - late final providerId = seasonDetailsProvider(widget.item.id); + AutoDisposeStateNotifierProvider get providerId => + seasonDetailsProvider(widget.item.id); @override Widget build(BuildContext context) { diff --git a/lib/screens/details_screens/series_detail_screen.dart b/lib/screens/details_screens/series_detail_screen.dart index 9278da9..33a5d7f 100644 --- a/lib/screens/details_screens/series_detail_screen.dart +++ b/lib/screens/details_screens/series_detail_screen.dart @@ -1,26 +1,28 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:ficonsax/ficonsax.dart'; -import 'package:fladder/screens/details_screens/components/overview_header.dart'; -import 'package:fladder/screens/shared/media/components/media_play_button.dart'; -import 'package:fladder/screens/shared/media/components/next_up_episode.dart'; -import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; -import 'package:fladder/util/item_base_model/play_item_helpers.dart'; -import 'package:fladder/util/localization_helper.dart'; import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/models/item_base_model.dart'; +import 'package:fladder/models/items/series_model.dart'; import 'package:fladder/providers/items/series_details_provider.dart'; import 'package:fladder/providers/user_provider.dart'; +import 'package:fladder/screens/details_screens/components/overview_header.dart'; import 'package:fladder/screens/shared/detail_scaffold.dart'; import 'package:fladder/screens/shared/media/components/media_header.dart'; +import 'package:fladder/screens/shared/media/components/media_play_button.dart'; +import 'package:fladder/screens/shared/media/components/next_up_episode.dart'; import 'package:fladder/screens/shared/media/episode_posters.dart'; import 'package:fladder/screens/shared/media/expanding_overview.dart'; import 'package:fladder/screens/shared/media/people_row.dart'; import 'package:fladder/screens/shared/media/poster_row.dart'; import 'package:fladder/screens/shared/media/season_row.dart'; +import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; +import 'package:fladder/util/item_base_model/play_item_helpers.dart'; import 'package:fladder/util/list_padding.dart'; +import 'package:fladder/util/localization_helper.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/selectable_icon_button.dart'; @@ -33,7 +35,8 @@ class SeriesDetailScreen extends ConsumerStatefulWidget { } class _SeriesDetailScreenState extends ConsumerState { - late final providerId = seriesDetailsProvider(widget.item.id); + AutoDisposeStateNotifierProvider get providerId => + seriesDetailsProvider(widget.item.id); @override Widget build(BuildContext context) { @@ -51,7 +54,7 @@ class _SeriesDetailScreenState extends ConsumerState { }, onDeleteSuccesFully: (item) { if (context.mounted) { - context.router.maybePop(); + context.router.popBack(); } }, ), diff --git a/lib/screens/library_search/library_search_screen.dart b/lib/screens/library_search/library_search_screen.dart index 6141910..2ea5060 100644 --- a/lib/screens/library_search/library_search_screen.dart +++ b/lib/screens/library_search/library_search_screen.dart @@ -1,3 +1,7 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + import 'package:auto_route/auto_route.dart'; import 'package:ficonsax/ficonsax.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -28,6 +32,7 @@ import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/refresh_state.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/sliver_list_padding.dart'; import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart'; import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart'; @@ -41,9 +46,6 @@ import 'package:fladder/widgets/shared/pull_to_refresh.dart'; import 'package:fladder/widgets/shared/scroll_position.dart'; import 'package:fladder/widgets/shared/shapes.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - @RoutePage() class LibrarySearchScreen extends ConsumerStatefulWidget { final String? viewModelId; @@ -67,9 +69,6 @@ class LibrarySearchScreen extends ConsumerStatefulWidget { } class _LibrarySearchScreenState extends ConsumerState { - late final Key uniqueKey = Key(widget.folderId?.join(',').toString() ?? widget.viewModelId ?? UniqueKey().toString()); - late final providerKey = librarySearchProvider(uniqueKey); - late final libraryProvider = ref.read(providerKey.notifier); final SearchController searchController = SearchController(); final Debouncer debouncer = Debouncer(const Duration(seconds: 1)); final GlobalKey refreshKey = GlobalKey(); @@ -78,9 +77,26 @@ class _LibrarySearchScreenState extends ConsumerState { bool loadOnStart = false; + Key get uniqueKey => Key(widget.folderId?.join(',').toString() ?? widget.viewModelId ?? "EmptySearch"); + AutoDisposeStateNotifierProvider get providerKey => + librarySearchProvider(uniqueKey); + LibrarySearchNotifier get libraryProvider => ref.read(librarySearchProvider(uniqueKey).notifier); + + @override + void didUpdateWidget(covariant LibrarySearchScreen oldWidget) { + super.didUpdateWidget(oldWidget); + if (kIsWeb && ref.read(librarySearchProvider(uniqueKey)).posters.isEmpty) { + initLibrary(); + } + } + @override void initState() { super.initState(); + initLibrary(); + } + + void initLibrary() { searchController.addListener(() { debouncer.run(() { ref.read(providerKey.notifier).setSearch(searchController.text); @@ -89,7 +105,9 @@ class _LibrarySearchScreenState extends ConsumerState { Future.microtask( () async { - libraryProvider.setDefaultOptions(widget.sortOrder, widget.sortingOptions); + if (libraryProvider.mounted) { + libraryProvider.setDefaultOptions(widget.sortOrder, widget.sortingOptions); + } await refreshKey.currentState?.show(); SystemChrome.setEnabledSystemUIMode( SystemUiMode.edgeToEdge, @@ -115,10 +133,8 @@ class _LibrarySearchScreenState extends ConsumerState { Widget build(BuildContext context) { final isEmptySearchScreen = widget.viewModelId == null && widget.favourites == null && widget.folderId == null; final librarySearchResults = ref.watch(providerKey); - final libraryProvider = ref.read(providerKey.notifier); final postersList = librarySearchResults.posters.hideEmptyChildren(librarySearchResults.hideEmtpyShows); final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state)); - final libraryViewType = ref.watch(libraryViewTypeProvider); ref.listen( @@ -132,6 +148,7 @@ class _LibrarySearchScreenState extends ConsumerState { ); return PopScope( + key: uniqueKey, canPop: !librarySearchResults.selecteMode, onPopInvokedWithResult: (didPop, result) { if (librarySearchResults.selecteMode) { @@ -210,8 +227,12 @@ class _LibrarySearchScreenState extends ConsumerState { refreshKey: refreshKey, autoFocus: false, contextRefresh: false, - onRefresh: () async => - libraryProvider.initRefresh(widget.folderId, widget.viewModelId, widget.favourites), + onRefresh: () async { + if (libraryProvider.mounted) { + return libraryProvider.initRefresh( + widget.folderId, widget.viewModelId, widget.favourites); + } + }, refreshOnStart: false, child: CustomScrollView( physics: const AlwaysScrollableNoImplicitScrollPhysics(), @@ -220,10 +241,11 @@ class _LibrarySearchScreenState extends ConsumerState { SliverAppBar( floating: !AdaptiveLayout.of(context).isDesktop, collapsedHeight: 80, - automaticallyImplyLeading: true, + automaticallyImplyLeading: false, pinned: AdaptiveLayout.of(context).isDesktop, primary: true, elevation: 5, + leading: context.router.backButton(), surfaceTintColor: Colors.transparent, shadowColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface, diff --git a/lib/screens/library_search/widgets/library_filter_chips.dart b/lib/screens/library_search/widgets/library_filter_chips.dart index 9e72b6f..92d9ae9 100644 --- a/lib/screens/library_search/widgets/library_filter_chips.dart +++ b/lib/screens/library_search/widgets/library_filter_chips.dart @@ -1,4 +1,8 @@ +import 'package:flutter/material.dart'; + import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/item_shared_models.dart'; @@ -12,8 +16,6 @@ import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/map_bool_helper.dart'; import 'package:fladder/util/refresh_state.dart'; import 'package:fladder/widgets/shared/scroll_position.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class LibraryFilterChips extends ConsumerWidget { final Key uniqueKey; @@ -188,12 +190,12 @@ List libraryFilterChips( if (librarySearchResults.types[FladderItemType.series] == true) FilterChip( avatar: Icon( - librarySearchResults.hideEmtpyShows ? Icons.visibility_rounded : Icons.visibility_off_rounded, + librarySearchResults.hideEmtpyShows ? Icons.visibility_off_rounded : Icons.visibility_rounded, color: Theme.of(context).colorScheme.onSurface, ), selected: librarySearchResults.hideEmtpyShows, showCheckmark: false, - label: Text(librarySearchResults.hideEmtpyShows ? context.localized.showEmpty : context.localized.hideEmpty), + label: Text(context.localized.hideEmpty), onSelected: libraryProvider.setHideEmpty, ), if (librarySearchResults.officialRatings.isNotEmpty) diff --git a/lib/screens/login/login_screen.dart b/lib/screens/login/login_screen.dart index 181898f..c54b734 100644 --- a/lib/screens/login/login_screen.dart +++ b/lib/screens/login/login_screen.dart @@ -164,7 +164,7 @@ class _LoginPageState extends ConsumerState { serverTextController.text = value; startAddingNewUser(); }); - context.router.maybePop(); + Navigator.of(context).pop(); }, ), ); diff --git a/lib/screens/metadata/identifty_screen.dart b/lib/screens/metadata/identifty_screen.dart index f0f4ca1..127508e 100644 --- a/lib/screens/metadata/identifty_screen.dart +++ b/lib/screens/metadata/identifty_screen.dart @@ -1,6 +1,10 @@ +import 'package:flutter/material.dart'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:collection/collection.dart'; import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/identify_provider.dart'; import 'package:fladder/screens/shared/adaptive_dialog.dart'; @@ -9,8 +13,6 @@ import 'package:fladder/screens/shared/focused_outlined_text_field.dart'; import 'package:fladder/screens/shared/media/external_urls.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/string_extensions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; Future showIdentifyScreen(BuildContext context, ItemBaseModel item) async { return showDialogAdaptive( @@ -30,7 +32,7 @@ class IdentifyScreen extends ConsumerStatefulWidget { } class _IdentifyScreenState extends ConsumerState with TickerProviderStateMixin { - late AutoDisposeStateNotifierProvider provider = identifyProvider(widget.item.id); + AutoDisposeStateNotifierProvider get provider => identifyProvider(widget.item.id); late final TabController tabController = TabController(length: 2, vsync: this); TextEditingController? currentController; diff --git a/lib/screens/metadata/info_screen.dart b/lib/screens/metadata/info_screen.dart index 00e2262..9243675 100644 --- a/lib/screens/metadata/info_screen.dart +++ b/lib/screens/metadata/info_screen.dart @@ -1,13 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/information_model.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/providers/items/information_provider.dart'; import 'package:fladder/screens/shared/fladder_snackbar.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/widgets/shared/clickable_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter/services.dart'; Future showInfoScreen(BuildContext context, ItemBaseModel item) async { return showDialog( @@ -27,7 +29,7 @@ class ItemInfoScreen extends ConsumerStatefulWidget { } class ItemInfoScreenState extends ConsumerState { - late AutoDisposeStateNotifierProvider provider = + AutoDisposeStateNotifierProvider get provider => informationProvider(widget.item.id); @override diff --git a/lib/screens/settings/client_settings_page.dart b/lib/screens/settings/client_settings_page.dart index d913c6a..3560601 100644 --- a/lib/screens/settings/client_settings_page.dart +++ b/lib/screens/settings/client_settings_page.dart @@ -133,10 +133,10 @@ class _ClientSettingsPageState extends ConsumerState { (context) async { await ref.read(syncProvider.notifier).clear(); setState(() {}); - context.router.maybePop(); + Navigator.of(context).pop(); }, context.localized.clear, - (context) => context.router.maybePop(), + (context) => Navigator.of(context).pop(), context.localized.cancel, ); }, @@ -451,7 +451,7 @@ class _ClientSettingsPageState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ FilledButton( - onPressed: () => context.router.maybePop(), + onPressed: () => Navigator.of(context).pop(), child: Text(context.localized.cancel), ), const SizedBox(width: 8), diff --git a/lib/screens/settings/settings_scaffold.dart b/lib/screens/settings/settings_scaffold.dart index f6b37df..ee811ad 100644 --- a/lib/screens/settings/settings_scaffold.dart +++ b/lib/screens/settings/settings_scaffold.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/shared/user_icon.dart'; import 'package:fladder/util/adaptive_layout.dart'; +import 'package:fladder/util/router_extension.dart'; class SettingsScaffold extends ConsumerWidget { final String label; @@ -39,6 +41,7 @@ class SettingsScaffold extends ConsumerWidget { if (AdaptiveLayout.of(context).size == ScreenLayout.single) SliverAppBar.large( backgroundColor: Theme.of(context).scaffoldBackgroundColor, + leading: context.router.backButton(), flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16) .add(EdgeInsets.only(left: padding.left, right: padding.right)), diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 1dfb0e5..d776fe1 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -15,6 +15,7 @@ import 'package:fladder/screens/shared/fladder_icon.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/application_info.dart'; import 'package:fladder/util/localization_helper.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/theme_extensions.dart'; import 'package:fladder/widgets/shared/hide_on_scroll.dart'; @@ -90,7 +91,7 @@ class _SettingsScreenState extends ConsumerState { scrollController: scrollController, showUserIcon: true, items: [ - if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.dual) + if (context.router.canNavigateBack && AdaptiveLayout.of(context).size == ScreenLayout.dual) Align( alignment: Alignment.centerLeft, child: Padding( @@ -99,7 +100,7 @@ class _SettingsScreenState extends ConsumerState { style: IconButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), ), - onPressed: () => context.router.maybePop(), + onPressed: () => context.router.popBack(), icon: Padding( padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4), child: const Icon(IconsaxOutline.arrow_left_2), diff --git a/lib/screens/shared/detail_scaffold.dart b/lib/screens/shared/detail_scaffold.dart index 0f0e50b..f33f18d 100644 --- a/lib/screens/shared/detail_scaffold.dart +++ b/lib/screens/shared/detail_scaffold.dart @@ -7,7 +7,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/images_models.dart'; import 'package:fladder/models/media_playback_model.dart'; -import 'package:fladder/providers/items/item_details_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/theme.dart'; @@ -15,74 +14,13 @@ import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/fladder_image.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/refresh_state.dart'; +import 'package:fladder/util/router_extension.dart'; import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart'; import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart'; import 'package:fladder/widgets/shared/item_actions.dart'; import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/pull_to_refresh.dart'; -class DetailScreen extends ConsumerStatefulWidget { - final String id; - final ItemBaseModel? item; - const DetailScreen({required this.id, this.item, super.key}); - - @override - ConsumerState createState() => _DetailScreenState(); -} - -class _DetailScreenState extends ConsumerState { - late Widget currentWidget = const Center( - key: Key("progress-indicator"), - child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round), - ); - - @override - void initState() { - super.initState(); - Future.microtask(() async { - if (widget.item != null) { - setState(() { - currentWidget = widget.item!.detailScreenWidget; - }); - } else { - final response = await ref.read(itemDetailsProvider.notifier).fetchDetails(widget.id); - if (context.mounted) { - if (response != null) { - setState(() { - currentWidget = response.detailScreenWidget; - }); - } else { - context.router.navigate(const DashboardRoute()); - } - } - } - }); - } - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Hero( - tag: widget.id, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface.withOpacity(1.0), - ), - //Small offset to match detailscaffold - child: Transform.translate( - offset: const Offset(0, -5), child: FladderImage(image: widget.item?.getPosters?.primary)), - ), - ), - AnimatedSwitcher( - duration: const Duration(seconds: 1), - child: currentWidget, - ) - ], - ); - } -} - class DetailScaffold extends ConsumerStatefulWidget { final String label; final ItemBaseModel? item; @@ -212,7 +150,7 @@ class _DetailScaffoldState extends ConsumerState { style: IconButton.styleFrom( backgroundColor: backGroundColor, ), - onPressed: () => context.router.maybePop(), + onPressed: () => context.router.popBack(), icon: Padding( padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4), @@ -269,8 +207,8 @@ class _DetailScaffoldState extends ConsumerState { icon: const Icon(IconsaxOutline.refresh), ), ), - ) - else + ), + if (AdaptiveLayout.of(context).size == ScreenLayout.single) const SizedBox(height: 30, width: 30, child: SettingsUserIcon()), Tooltip( message: context.localized.home, diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index 0849160..efa5301 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/models/account_model.dart'; import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/user_provider.dart'; +import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/screens/shared/fladder_logo.dart'; @RoutePage() @@ -28,19 +29,16 @@ class _SplashScreenState extends ConsumerState { if (context.mounted) { if (lastUsedAccount == null) { - widget.loggedIn?.call(false); - context.router.maybePop(false); + callBackOrNavigate(false); } else { switch (lastUsedAccount.authMethod) { case Authentication.autoLogin: - widget.loggedIn?.call(true); - context.router.maybePop(true); + callBackOrNavigate(true); break; case Authentication.biometrics: case Authentication.none: case Authentication.passcode: - widget.loggedIn?.call(false); - context.router.maybePop(false); + callBackOrNavigate(false); break; } } @@ -48,6 +46,19 @@ class _SplashScreenState extends ConsumerState { }); } + void callBackOrNavigate(bool loggedIn) { + if (widget.loggedIn == null) { + if (loggedIn) { + context.router.replace(const DashboardRoute()); + } else { + context.router.replace(const LoginRoute()); + } + } else { + widget.loggedIn?.call(loggedIn); + context.router.maybePop(loggedIn); + } + } + @override Widget build(BuildContext context) { return const Scaffold( diff --git a/lib/util/router_extension.dart b/lib/util/router_extension.dart new file mode 100644 index 0000000..7b13b7a --- /dev/null +++ b/lib/util/router_extension.dart @@ -0,0 +1,30 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; + +extension RouterExtension on StackRouter { + Future popBack() async { + if (kIsWeb) { + back(); + return canNavigateBack; + } else { + return maybePop(); + } + } + + Widget? backButton() { + if (kIsWeb && canNavigateBack) { + return IconButton( + onPressed: back, + icon: const BackButtonIcon(), + ); + } else if (canPop()) { + return IconButton( + onPressed: maybePop, + icon: const BackButtonIcon(), + ); + } + return null; + } +} diff --git a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart index a2f47c5..1f1c0a7 100644 --- a/lib/widgets/navigation_scaffold/components/navigation_drawer.dart +++ b/lib/widgets/navigation_scaffold/components/navigation_drawer.dart @@ -154,7 +154,8 @@ class NestedNavigationDrawer extends ConsumerWidget { bool checkLibrary(BuildContext context, String id) { try { - return context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id; + return context.router.current.name == LibrarySearchRoute().routeName && + (context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id); } catch (e) { return false; } diff --git a/lib/widgets/navigation_scaffold/navigation_scaffold.dart b/lib/widgets/navigation_scaffold/navigation_scaffold.dart index 3d7b960..bb0ef94 100644 --- a/lib/widgets/navigation_scaffold/navigation_scaffold.dart +++ b/lib/widgets/navigation_scaffold/navigation_scaffold.dart @@ -1,3 +1,7 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/models/media_playback_model.dart'; import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/providers/views_provider.dart'; @@ -9,8 +13,6 @@ import 'package:fladder/widgets/navigation_scaffold/components/floating_player_b import 'package:fladder/widgets/navigation_scaffold/components/navigation_body.dart'; import 'package:fladder/widgets/navigation_scaffold/components/navigation_drawer.dart'; import 'package:fladder/widgets/shared/hide_on_scroll.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class NavigationScaffold extends ConsumerStatefulWidget { final String? currentRouteName; diff --git a/lib/widgets/shared/trickplay_image.dart b/lib/widgets/shared/trickplay_image.dart index 2a8b40f..48d18d0 100644 --- a/lib/widgets/shared/trickplay_image.dart +++ b/lib/widgets/shared/trickplay_image.dart @@ -4,6 +4,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; + import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; @@ -34,12 +35,12 @@ class _TrickplayImageState extends ConsumerState { @override void didUpdateWidget(covariant TrickplayImage oldWidget) { + super.didUpdateWidget(oldWidget); if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) { time = widget.position ?? Duration.zero; model = widget.trickplay; loadImage(); } - super.didUpdateWidget(oldWidget); } @override