Files
2026-06-07 12:03:23 +08:00

227 lines
7.6 KiB
Dart

import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../data/providers.dart';
import '../features/detail/report_detail_page.dart';
import '../features/feed/feed_page.dart';
import '../features/home/home_page.dart';
import '../features/auth/login_page.dart';
import '../features/institutions/institution_detail_page.dart';
import '../features/institutions/institutions_page.dart';
import '../features/listen/listen_page.dart';
import '../features/profile/profile_page.dart';
import '../features/reports/reports_page.dart';
import '../features/settings/settings_page.dart';
import '../features/shell_page.dart';
import 'app_routes.dart';
final routerProvider = Provider<GoRouter>((ref) {
final dataSource = ref.read(reportDataSourceProvider);
return GoRouter(
initialLocation: AppRoutes.home,
routes: [
ShellRoute(
builder: (context, state, child) =>
ShellPage(currentPath: state.matchedLocation, child: child),
routes: [
GoRoute(
path: AppRoutes.home,
builder: (context, state) => _TabSurface(
child: Consumer(
builder: (context, ref, _) {
final player = ref.watch(audioPlayerControllerProvider);
final controller = ref.read(
audioPlayerControllerProvider.notifier,
);
return FeedPage(
dataSource: dataSource,
onPlay: controller.startFromItem,
player: player,
onStartModuleAudio: controller.startModuleAudio,
onToggleAudio: controller.toggleAudio,
onSeekAudio: controller.seekAudio,
onSpeed: controller.cycleSpeed,
);
},
),
),
),
GoRoute(
path: AppRoutes.homeFeed,
builder: (context, state) => _TabSurface(
child: Consumer(
builder: (context, ref, _) {
final player = ref.watch(audioPlayerControllerProvider);
final controller = ref.read(
audioPlayerControllerProvider.notifier,
);
return HomePage(
dataSource: dataSource,
onPlay: controller.startFromItem,
player: player,
onStartModuleAudio: controller.startModuleAudio,
onToggleAudio: controller.toggleAudio,
onSeekAudio: controller.seekAudio,
onSpeed: controller.cycleSpeed,
);
},
),
),
),
GoRoute(
path: AppRoutes.reports,
builder: (context, state) => _TabSurface(
child: Consumer(
builder: (context, ref, _) {
final player = ref.watch(audioPlayerControllerProvider);
final controller = ref.read(
audioPlayerControllerProvider.notifier,
);
return ReportsPage(
dataSource: dataSource,
onPlay: controller.startFromItem,
player: player,
onStartModuleAudio: controller.startModuleAudio,
onToggleAudio: controller.toggleAudio,
onSeekAudio: controller.seekAudio,
onSpeed: controller.cycleSpeed,
);
},
),
),
),
GoRoute(
path: AppRoutes.institutions,
builder: (context, state) =>
_TabSurface(child: InstitutionsPage(dataSource: dataSource)),
),
GoRoute(
path: AppRoutes.listen,
builder: (context, state) => _TabSurface(
child: Consumer(
builder: (context, ref, _) {
final controller = ref.read(
audioPlayerControllerProvider.notifier,
);
return ListenPage(
dataSource: dataSource,
onPlay: controller.startFromItem,
);
},
),
),
),
GoRoute(
path: AppRoutes.profile,
builder: (context, state) =>
_TabSurface(child: ProfilePage(dataSource: dataSource)),
),
],
),
GoRoute(
path: AppRoutes.reportDetail,
pageBuilder: (context, state) {
final id = state.pathParameters['id'] ?? '';
final args = state.extra as ReportDetailRouteArgs?;
return _slidePage(
state: state,
child: Consumer(
builder: (context, ref, _) {
final player = ref.watch(audioPlayerControllerProvider);
final controller = ref.read(
audioPlayerControllerProvider.notifier,
);
return ReportDetailPage(
reportId: id,
dataSource: args?.dataSource ?? dataSource,
player: player,
onStartAudio:
args?.onStartAudio ?? controller.startModuleAudio,
onToggleAudio: args?.onToggleAudio ?? controller.toggleAudio,
onSeekAudio: args?.onSeekAudio ?? controller.seekAudio,
onSpeed: args?.onSpeed ?? controller.cycleSpeed,
);
},
),
);
},
),
GoRoute(
path: AppRoutes.login,
pageBuilder: (context, state) {
final next = state.uri.queryParameters['next'];
return _slidePage(
state: state,
child: LoginPage(next: next),
);
},
),
GoRoute(
path: AppRoutes.institutionDetail,
pageBuilder: (context, state) {
final id = state.pathParameters['id'] ?? '';
final args = state.extra as InstitutionDetailRouteArgs?;
return _slidePage(
state: state,
child: InstitutionDetailPage(
institutionId: id,
dataSource: args?.dataSource ?? dataSource,
),
);
},
),
GoRoute(
path: AppRoutes.settings,
pageBuilder: (context, state) =>
_slidePage(state: state, child: const SettingsPage()),
),
],
);
});
CustomTransitionPage<void> _slidePage({
required GoRouterState state,
required Widget child,
}) {
return CustomTransitionPage<void>(
key: state.pageKey,
transitionDuration: const Duration(milliseconds: 260),
reverseTransitionDuration: const Duration(milliseconds: 220),
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final curved = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
);
return FadeTransition(
opacity: curved,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.08, 0),
end: Offset.zero,
).animate(curved),
child: child,
),
);
},
);
}
class _TabSurface extends StatelessWidget {
const _TabSurface({required this.child});
final Widget child;
@override
Widget build(BuildContext context) {
return ColoredBox(
color: ShadTheme.of(context).colorScheme.background,
child: child,
);
}
}