[Feature] Replace go_router with auto_route

This commit is contained in:
PartyDonut 2024-10-05 16:29:51 +02:00
parent b31cc86ae8
commit 144c8faf70
42 changed files with 1181 additions and 980 deletions

View file

@ -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:

View file

@ -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<Main> 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<Main> with WindowListener, WidgetsBinding
),
),
themeMode: themeMode,
routerConfig: AdaptiveLayout.of(context).router,
routerConfig: AdaptiveLayout.routerOf(context).config(),
),
);
}),
);
}
List<RouteBase> getRoutes(LayoutState state) {
switch (state) {
case LayoutState.phone:
return AppRoutes.linearRoutes;
case LayoutState.tablet:
case LayoutState.desktop:
return AppRoutes.nestedRoutes;
}
}
}
final currentTitleProvider = StateProvider<String>((ref) {

View file

@ -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<void> navigateTo(BuildContext context) async => context.routePush(DetailsRoute(id: id), extra: this);
Future<void> navigateTo(BuildContext context) async => context.router.push(DetailsRoute(id: id, item: this));
factory ItemBaseModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
return switch (item.type) {

View file

@ -27,7 +27,7 @@ Map<String, dynamic> _$$SessionInfoModelImplToJson(
// RiverpodGenerator
// **************************************************************************
String _$sessionInfoHash() => r'ab5afcada1c9677cadda954c9abf7eb361dc057d';
String _$sessionInfoHash() => r'024da7f8d05fb98f6e2e5395ed06c1cc9d003f79';
/// See also [SessionInfo].
@ProviderFor(SessionInfo)

View file

@ -7,7 +7,7 @@ part of 'background_download_provider.dart';
// **************************************************************************
String _$backgroundDownloaderHash() =>
r'2bc7a06682cdcfa9a754dce9b7f7ea48f873682e';
r'9a9f91504ae4532ab37290ee9372d2e7a18380a9';
/// See also [backgroundDownloader].
@ProviderFor(backgroundDownloader)

View file

@ -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<NavigatorState>();
static final homeShellKey = GlobalKey<NavigatorState>();
static final settingsKey = GlobalKey<NavigatorState>();
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<RouteBase> getRoutes(ScreenLayout screenLayout) {
switch (screenLayout) {
case ScreenLayout.single:
return linearRoutes;
case ScreenLayout.dual:
return nestedRoutes;
}
}
static List<RouteBase> 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<RouteBase> 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,
],
),
];
}

135
lib/routes/auto_router.dart Normal file
View file

@ -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<AutoRouteGuard> get guards => [...super.guards, AuthGuard(ref: ref)];
@override
RouteType get defaultRouteType => kIsWeb ? const RouteType.material() : const RouteType.adaptive();
@override
List<AutoRoute> get routes => [
..._defaultRoutes,
...(layout == ScreenLayout.dual ? desktopRoutes : mobileRoutes),
];
final List<AutoRoute> 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<AutoRoute> 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<AutoRoute> _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<AutoRoute> _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<void> 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<void> 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<bool>(SplashRoute(loggedIn: (value) {
if (value) {
resolver.next(true);
} else {
router.navigate(const LoginRoute());
}
}));
}
}

View file

@ -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<void> {
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<void> {
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<DetailsRouteArgs> {
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<DetailsRouteArgs>(
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<void> {
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<void> {
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<LibrarySearchRouteArgs> {
LibrarySearchRoute({
String? viewModelId,
List<String>? 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<LibrarySearchRouteArgs>(
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<String>? 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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<SplashRouteArgs> {
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<SplashRouteArgs>(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<SyncedRouteArgs> {
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<SyncedRouteArgs>(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}';
}
}

View file

@ -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<NavigatorState>? 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<NavigatorState>? 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,
);
},
);
},
);
}

View file

@ -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<T?> routePush<T>(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<String, dynamic>? 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<String, dynamic>? 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);
}

View file

@ -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<NavigatorState>? 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<NavigatorState>? 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<NavigatorState>? 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<NavigatorState>? 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,
);
},
);
},
);
}

View file

@ -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<ConsumerStatefulWidget> createState() => _DetailsScreenState();
}
class _DetailsScreenState extends ConsumerState<DetailsScreen> {
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,
)
],
);
}
}

View file

@ -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<DashboardScreen> {
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<DashboardScreen> {
.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<DashboardScreen> {
SortingOptions.dateLastContentAdded,
_ => SortingOptions.dateAdded,
},
sortOrder: SortOrder.descending,
sortOrder: SortingOrder.descending,
)),
posters: view.recentlyAdded,
),

View file

@ -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<BookDetailScreen> {
},
onDeleteSuccesFully: (item) {
if (context.mounted) {
context.pop();
context.router.maybePop();
}
},
),

View file

@ -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<EpisodeDetailScreen> {
},
onDeleteSuccesFully: (item) {
if (context.mounted) {
context.pop();
context.router.maybePop();
}
},
),

View file

@ -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<MovieDetailScreen> {
},
onDeleteSuccesFully: (item) {
if (context.mounted) {
context.pop();
context.router.maybePop();
}
},
),

View file

@ -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<SeriesDetailScreen> {
},
onDeleteSuccesFully: (item) {
if (context.mounted) {
context.pop();
context.router.maybePop();
}
},
),

View file

@ -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(),

View file

@ -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 HeroControllerScope(
controller: HeroController(),
child: AutoRouter(
builder: (context, child) {
return NavigationScaffold(
currentIndex: currentTab?.index ?? 0,
location: location,
nestedChild: nestedChild,
destinations: destinations.whereNotNull().toList(),
currentRouteName: context.router.current.name,
nestedChild: child,
);
},
),
);
}
}

View file

@ -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<LibrarySearchScreen> {
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<LibrarySearchScreen> {
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<LibrarySearchScreen> {
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<LibrarySearchScreen> {
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<LibrarySearchScreen> {
(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<LibrarySearchScreen> {
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,7 +331,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
position: RelativeRect.fromLTRB(left, top, 40, 100),
items: <PopupMenuEntry>[
PopupMenuItem(
child: Text(librarySearchResults.nestedCurrentItem?.type.label(context) ??
child: Text(
librarySearchResults.nestedCurrentItem?.type.label(context) ??
context.localized.library(0))),
itemCountWidget.toPopupMenuItem(useIcons: true),
refreshAction.toPopupMenuItem(useIcons: true),
@ -356,7 +364,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
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<LibrarySearchScreen> {
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(

View file

@ -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<bool>((ref) => false);
@RoutePage()
class LockScreen extends ConsumerStatefulWidget {
final bool selfLock;
const LockScreen({this.selfLock = false, super.key});
const LockScreen({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _LockScreenState();
@ -52,10 +52,6 @@ class _LockScreenState extends ConsumerState<LockScreen> 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<LockScreen> 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<LockScreen> 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),

View file

@ -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<LoginScreen> {
serverTextController.text = value;
startAddingNewUser();
});
context.pop();
context.router.maybePop();
},
),
);
@ -211,7 +211,7 @@ class _LoginPageState extends ConsumerState<LoginScreen> {
void loggedInGoToHome() {
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
if (context.mounted) {
context.routeGo(DashboardRoute());
context.router.push(const DashboardRoute());
}
}

View file

@ -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<ClientSettingsPage> {
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<ClientSettingsPage> {
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<ClientSettingsPage> {
),
)
: () 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<ClientSettingsPage> {
(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<ClientSettingsPage> {
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<ClientSettingsPage> {
(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<ClientSettingsPage> {
(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<ClientSettingsPage> {
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<ClientSettingsPage> {
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<ClientSettingsPage> {
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<ClientSettingsPage> {
)),
),
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<ClientSettingsPage> {
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<ClientSettingsPage> {
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),
)

View file

@ -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});

View file

@ -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});

View file

@ -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,
)

View file

@ -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<ConsumerStatefulWidget> createState() => _SettingsScreenState();
@ -29,17 +28,18 @@ class SettingsScreen extends ConsumerStatefulWidget {
class _SettingsScreenState extends ConsumerState<SettingsScreen> {
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 AutoRouter(
builder: (context, content) {
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -47,10 +47,12 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
Expanded(flex: 1, child: _leftPane(context)),
Expanded(
flex: 2,
child: widget.child ?? Container(),
child: content,
),
],
);
},
);
}
}
@ -68,16 +70,27 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
}
}
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(
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.canPop() && AdaptiveLayout.of(context).isDesktop)
if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.single)
Align(
alignment: Alignment.centerLeft,
child: Padding(
@ -86,9 +99,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
style: IconButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8),
),
onPressed: () {
context.pop();
},
onPressed: () => context.router.maybePop(),
icon: Padding(
padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
child: const Icon(IconsaxOutline.arrow_left_2),
@ -99,9 +110,9 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
SettingsListTile(
label: Text(context.localized.settingsClientTitle),
subLabel: Text(context.localized.settingsClientDesc),
selected: containsRoute(ClientSettingsRoute()),
selected: containsRoute(const ClientSettingsRoute()),
icon: deviceIcon,
onTap: () => context.routeReplaceOrPush(ClientSettingsRoute()),
onTap: () => navigateTo(const ClientSettingsRoute()),
),
if (quickConnectAvailable)
SettingsListTile(
@ -112,16 +123,16 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
SettingsListTile(
label: Text(context.localized.settingsProfileTitle),
subLabel: Text(context.localized.settingsProfileDesc),
selected: containsRoute(SecuritySettingsRoute()),
selected: containsRoute(const SecuritySettingsRoute()),
icon: IconsaxOutline.security_user,
onTap: () => context.routeReplaceOrPush(SecuritySettingsRoute()),
onTap: () => navigateTo(const SecuritySettingsRoute()),
),
SettingsListTile(
label: Text(context.localized.settingsPlayerTitle),
subLabel: Text(context.localized.settingsPlayerDesc),
selected: containsRoute(PlayerSettingsRoute()),
selected: containsRoute(const PlayerSettingsRoute()),
icon: IconsaxOutline.video_play,
onTap: () => context.routeReplaceOrPush(PlayerSettingsRoute()),
onTap: () => navigateTo(const PlayerSettingsRoute()),
),
SettingsListTile(
label: Text(context.localized.about),
@ -156,9 +167,9 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
FloatingActionButton(
key: Key(context.localized.switchUser),
tooltip: context.localized.switchUser,
onPressed: () {
ref.read(userProvider.notifier).logoutUser();
context.routeGo(LoginRoute());
onPressed: () async {
await ref.read(userProvider.notifier).logoutUser();
context.router.navigate(const LoginRoute());
},
child: const Icon(
IconsaxOutline.arrow_swap_horizontal,
@ -166,6 +177,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
),
const SizedBox(width: 16),
FloatingActionButton(
heroTag: context.localized.logout,
key: Key(context.localized.logout),
tooltip: context.localized.logout,
backgroundColor: Theme.of(context).colorScheme.errorContainer,
@ -186,12 +198,14 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
),
ElevatedButton(
style: ElevatedButton.styleFrom().copyWith(
foregroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer),
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.routeGo(SplashRoute());
if (context.mounted) context.router.navigate(const LoginRoute());
},
child: Text(context.localized.logout),
),
@ -215,6 +229,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
);
},
),
),
);
}
}

View file

@ -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<DetailScreen> {
currentWidget = response.detailScreenWidget;
});
} else {
context.routeGo(DashboardRoute());
context.router.navigate(const DashboardRoute());
}
}
}
@ -212,13 +212,7 @@ class _DetailScaffoldState extends ConsumerState<DetailScaffold> {
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<DetailScaffold> {
Tooltip(
message: context.localized.home,
child: IconButton(
onPressed: () => context.routeGo(DashboardRoute()),
onPressed: () => context.router.navigate(const DashboardRoute()),
icon: const Icon(IconsaxOutline.home),
),
),

View file

@ -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<Widget> trailing;
@ -58,9 +57,9 @@ class _FloatingSearchBarState extends ConsumerState<FloatingSearchBar> {
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<FloatingSearchBar> {
),
IconButton(
onPressed: () {
context.routeGo(SecuritySettingsRoute());
context.router.push(const SecuritySettingsRoute());
},
icon: ClipRRect(
borderRadius: BorderRadius.circular(200),

View file

@ -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}...")),
],
),
),

View file

@ -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<ConsumerStatefulWidget> createState() => _SplashScreenState();
@ -18,23 +21,26 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
@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;
}
}

View file

@ -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<ConsumerStatefulWidget> createState() => _SyncedScreenState();

View file

@ -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<AdaptiveLayoutBuilder> {
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<AdaptiveLayoutBuilder> {
}
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<AdaptiveLayoutBuilder> {
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<AdaptiveLayoutBuilder> {
);
}
}
double? get topPadding {
return switch (defaultTargetPlatform) {
TargetPlatform.linux || TargetPlatform.windows || TargetPlatform.macOS => 35,
_ => null
};
}

View file

@ -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;

View file

@ -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,

View file

@ -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<NavigationBody> {
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<NavigationBody> {
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(

View file

@ -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();

View file

@ -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()),
),
);
}

View file

@ -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<DestinationModel> destinations;
final GlobalKey<NavigatorState>? 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<NavigationScaffold> {
final GlobalKey<ScaffoldState> _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<NavigationScaffold> {
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<NavigationScaffold> {
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<NavigationScaffold> {
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(),
),

View file

@ -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});

View file

@ -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:

View file

@ -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