mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
Init repo
This commit is contained in:
commit
764b6034e3
566 changed files with 212335 additions and 0 deletions
121
lib/routes/app_routes.dart
Normal file
121
lib/routes/app_routes.dart
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
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,
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
182
lib/routes/build_routes/home_routes.dart
Normal file
182
lib/routes/build_routes/home_routes.dart
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
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,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
124
lib/routes/build_routes/route_builder.dart
Normal file
124
lib/routes/build_routes/route_builder.dart
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
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);
|
||||
}
|
||||
103
lib/routes/build_routes/settings_routes.dart
Normal file
103
lib/routes/build_routes/settings_routes.dart
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
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,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue