Initial commit
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../features/account/account_auth_page.dart';
|
||||
import '../features/account/account_center_page.dart';
|
||||
import '../features/aggregated/aggregated_view_page.dart';
|
||||
import '../features/calendar/trakt_calendar_page.dart';
|
||||
import '../features/detail/detail_entry.dart';
|
||||
import '../features/detail/emby_detail_view.dart';
|
||||
import '../features/detail/tmdb_detail_view.dart';
|
||||
import '../features/discover/discover_page.dart';
|
||||
import '../features/home/home_page.dart';
|
||||
import '../features/library/library_list_page.dart';
|
||||
import '../features/modules/module_all_page.dart';
|
||||
import '../features/modules/module_browse_page.dart';
|
||||
import '../features/modules/module_detail_page.dart';
|
||||
import '../features/person_detail/person_detail_page.dart';
|
||||
import '../features/player/player_launcher.dart';
|
||||
import '../features/player/player_page.dart';
|
||||
import '../features/profile/profile_page.dart';
|
||||
import '../features/search/global_search_page.dart';
|
||||
import '../features/servers/servers_page.dart';
|
||||
import '../features/settings/settings_full_page.dart';
|
||||
import '../shared/constants/breakpoints.dart';
|
||||
import '../core/contracts/library.dart';
|
||||
import '../core/contracts/script_widget.dart';
|
||||
import '../core/contracts/tmdb.dart';
|
||||
import '../providers/appearance_provider.dart';
|
||||
import '../shared/theme/app_theme.dart';
|
||||
import '../shared/widgets/app_error_state.dart';
|
||||
import '../shared/widgets/app_shell.dart';
|
||||
import '../shared/widgets/window_chrome_overlay.dart';
|
||||
import 'page_transitions.dart';
|
||||
|
||||
final rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||
final _homeBranchKey = GlobalKey<NavigatorState>(debugLabel: 'home');
|
||||
final _serversBranchKey = GlobalKey<NavigatorState>(debugLabel: 'servers');
|
||||
final _aggregatedBranchKey = GlobalKey<NavigatorState>(
|
||||
debugLabel: 'aggregated',
|
||||
);
|
||||
final _discoverBranchKey = GlobalKey<NavigatorState>(debugLabel: 'discover');
|
||||
final _calendarBranchKey = GlobalKey<NavigatorState>(debugLabel: 'calendar');
|
||||
final _settingsBranchKey = GlobalKey<NavigatorState>(debugLabel: 'settings');
|
||||
final _modulesBranchKey = GlobalKey<NavigatorState>(debugLabel: 'modules');
|
||||
final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
return GoRouter(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
initialLocation: ref.watch(initialLocationProvider),
|
||||
routes: [
|
||||
StatefulShellRoute.indexedStack(
|
||||
builder: (ctx, state, navigationShell) {
|
||||
final parentTheme = Theme.of(ctx);
|
||||
final materialTheme = parentTheme.brightness == Brightness.dark
|
||||
? AppTheme.dark()
|
||||
: AppTheme.light();
|
||||
return Theme(
|
||||
data: materialTheme.copyWith(platform: parentTheme.platform),
|
||||
child: AppShell(
|
||||
navigationShell: navigationShell,
|
||||
location: state.matchedLocation,
|
||||
),
|
||||
);
|
||||
},
|
||||
branches: [
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _homeBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: HomePage(),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/library/:libraryId',
|
||||
pageBuilder: (_, state) => CustomTransitionPage(
|
||||
child: LibraryListPage(
|
||||
libraryId: state.pathParameters['libraryId']!,
|
||||
title: state.uri.queryParameters['title'],
|
||||
isCollection:
|
||||
state.uri.queryParameters['collection'] == '1',
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _serversBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/servers',
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: ServersPage(),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _aggregatedBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/aggregated',
|
||||
pageBuilder: (_, state) {
|
||||
final tab = state.uri.queryParameters['tab'];
|
||||
return CustomTransitionPage(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) => AggregatedViewPage(
|
||||
initialTab: tab,
|
||||
isCompactMode: Breakpoints.isCompact(
|
||||
constraints.maxWidth,
|
||||
),
|
||||
),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/history',
|
||||
redirect: (_, _) => '/aggregated?tab=history',
|
||||
),
|
||||
GoRoute(
|
||||
path: '/favorites-all',
|
||||
redirect: (_, _) => '/aggregated?tab=favorites',
|
||||
),
|
||||
GoRoute(
|
||||
path: '/recent',
|
||||
redirect: (_, _) => '/aggregated?tab=recent',
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _discoverBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/discover',
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: DiscoverPage(),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _calendarBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/calendar',
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: TraktCalendarPage(),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _settingsBranchKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/settings',
|
||||
pageBuilder: (_, _) => CustomTransitionPage(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) =>
|
||||
Breakpoints.isCompact(constraints.maxWidth)
|
||||
? const SettingsFullPage()
|
||||
: const ProfilePage(),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
GoRoute(path: '/profile', redirect: (_, _) => '/settings'),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _modulesBranchKey,
|
||||
routes: [
|
||||
GoRoute(path: '/modules', redirect: (_, _) => '/'),
|
||||
GoRoute(
|
||||
path: '/modules/:widgetId',
|
||||
pageBuilder: (_, state) => CustomTransitionPage(
|
||||
key: state.pageKey,
|
||||
child: ModuleBrowsePage(
|
||||
widgetId: state.pathParameters['widgetId'] ?? '',
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/modules/:widgetId/module/:moduleKey',
|
||||
pageBuilder: (_, state) => CustomTransitionPage(
|
||||
key: state.pageKey,
|
||||
child: ModuleAllPage(
|
||||
widgetId: state.pathParameters['widgetId'] ?? '',
|
||||
moduleKey: state.pathParameters['moduleKey'] ?? '',
|
||||
initialParams: Map<String, String>.from(
|
||||
state.uri.queryParameters,
|
||||
),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/global-search',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) {
|
||||
final currentOnly =
|
||||
state.uri.queryParameters['currentServer'] == 'true';
|
||||
return CustomTransitionPage(
|
||||
child: WindowChromeHost(
|
||||
child: GlobalSearchPage(currentServerOnly: currentOnly),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.fade,
|
||||
transitionDuration: PageTransitions.shellFade,
|
||||
reverseTransitionDuration: PageTransitions.shellFade,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/media/:itemId',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) => CustomTransitionPage(
|
||||
key: state.pageKey,
|
||||
child: EmbyDetailView(
|
||||
itemId: state.pathParameters['itemId']!,
|
||||
initialSeriesId: state.uri.queryParameters['seriesId'],
|
||||
initialSeasonId: state.uri.queryParameters['seasonId'],
|
||||
seedItem: state.extra is EmbyRawItem
|
||||
? state.extra as EmbyRawItem
|
||||
: null,
|
||||
),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/tmdb/:mediaType/:id',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) {
|
||||
final extra = state.extra;
|
||||
return CustomTransitionPage(
|
||||
key: state.pageKey,
|
||||
child: TmdbDetailView(
|
||||
entry: TmdbDetailEntry(
|
||||
mediaType: state.pathParameters['mediaType']!,
|
||||
tmdbId: state.pathParameters['id']!,
|
||||
seed: extra is TmdbRecommendation ? extra : null,
|
||||
),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/person/:embyPersonId',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) {
|
||||
final embyPersonId = state.pathParameters['embyPersonId'] ?? '';
|
||||
return CustomTransitionPage(
|
||||
child: WindowChromeHost(
|
||||
child: PersonDetailPage(embyPersonId: embyPersonId),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/account/auth',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: WindowChromeHost(child: AccountAuthPage()),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/account-center',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, _) => const CustomTransitionPage(
|
||||
child: WindowChromeHost(child: AccountCenterPage()),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/modules/:widgetId/detail',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) {
|
||||
final seedItem = state.extra;
|
||||
return CustomTransitionPage(
|
||||
key: state.pageKey,
|
||||
child: seedItem is ScriptVideoItem
|
||||
? ModuleDetailPage(
|
||||
widgetId: state.pathParameters['widgetId'] ?? '',
|
||||
seedItem: seedItem,
|
||||
)
|
||||
: const Scaffold(
|
||||
body: AppErrorState(
|
||||
title: '缺少媒体信息',
|
||||
message: '请从模块内容列表重新打开该条目。',
|
||||
icon: Icons.link_off_rounded,
|
||||
),
|
||||
),
|
||||
transitionsBuilder: PageTransitions.slideRight,
|
||||
transitionDuration: PageTransitions.detailForward,
|
||||
reverseTransitionDuration: PageTransitions.detailReverse,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/player/:itemId',
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (_, state) {
|
||||
final q = state.uri.queryParameters;
|
||||
return CustomTransitionPage(
|
||||
transitionsBuilder: PageTransitions.player,
|
||||
transitionDuration: PageTransitions.playerForward,
|
||||
reverseTransitionDuration: PageTransitions.playerReverse,
|
||||
child: PlayerPage(
|
||||
itemId: state.pathParameters['itemId']!,
|
||||
serverId: q['serverId'],
|
||||
mediaSourceId: q['mediaSourceId'],
|
||||
backdropUrl: q['backdropUrl'],
|
||||
preferredSubtitleStreamIndex: int.tryParse(
|
||||
q['subtitleIndex'] ?? '',
|
||||
),
|
||||
preferredAudioStreamIndex: int.tryParse(q['audioIndex'] ?? ''),
|
||||
startPositionTicks: int.tryParse(q['startPositionTicks'] ?? ''),
|
||||
fromStart: q['fromStart'] == '1',
|
||||
directStream: state.extra is DirectStreamLaunchData
|
||||
? state.extra as DirectStreamLaunchData
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
errorBuilder: (_, state) => Scaffold(
|
||||
body: AppErrorState(
|
||||
title: '页面不存在',
|
||||
message: state.matchedLocation,
|
||||
icon: Icons.search_off_outlined,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user