// 暖记路由表 — go_router + 认证守卫 // 对齐 Open Design: TabBar = 首页/日历/发现/我的,中心 FAB = 写日记 // // 路由守卫逻辑: // - 未认证用户访问受保护路由 → 重定向到 /login // - 已认证用户访问 /login → 重定向到 /home // - 需要角色选择 → 重定向到 /role-selection // - 需要班级码 → 重定向到 /class-code export '../../widgets/responsive_scaffold.dart'; import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../widgets/responsive_scaffold.dart'; import '../../features/home/views/home_page.dart'; import '../../features/calendar/views/calendar_page.dart'; import '../../features/mood/views/mood_page.dart'; import '../../features/search/views/search_page.dart'; import '../../features/calendar/views/weekly_page.dart'; import '../../features/calendar/views/monthly_page.dart'; import '../../features/profile/views/profile_page.dart'; import '../../features/editor/views/editor_page.dart'; import '../../features/auth/views/login_page.dart'; import '../../features/auth/views/role_selection_page.dart'; import '../../features/auth/views/class_code_join_page.dart'; import '../../features/onboarding/views/splash_page.dart'; import '../../features/onboarding/views/onboarding_page.dart'; import '../../features/class_/views/class_page.dart'; import '../../features/teacher/views/teacher_page.dart'; import '../../features/parent/views/parent_page.dart'; import '../../features/parent/bloc/parent_bloc.dart'; import '../../features/achievement/views/achievement_page.dart'; import '../../features/stickers/views/sticker_library_page.dart'; import '../../features/templates/views/template_gallery_page.dart'; import '../../features/settings/views/settings_page.dart'; import '../../features/auth/bloc/auth_bloc.dart'; import '../../features/search/bloc/search_bloc.dart'; import '../../data/repositories/journal_repository.dart'; import '../../data/remote/api_client.dart'; // Shell 分支键 final _rootNavigatorKey = GlobalKey(); final _shellNavigatorKey = GlobalKey(); /// 不需要认证的白名单路径 const _publicPaths = ['/splash', '/onboarding', '/login', '/role-selection', '/class-code']; /// 创建路由配置 — 需要注入 AuthBloc GoRouter createAppRouter(AuthBloc authBloc) { return GoRouter( navigatorKey: _rootNavigatorKey, initialLocation: '/splash', debugLogDiagnostics: true, // ===== 认证路由守卫 ===== redirect: (context, state) { final authState = authBloc.state; final currentPath = state.uri.path; // 加载中 → 不做重定向 if (authState is AuthInitial || authState is AuthLoading) { return null; } final isAuthenticated = authState is Authenticated; final isPublicPath = _publicPaths.contains(currentPath); // 已认证 + 访问公开页面 → 根据状态重定向 if (isAuthenticated && isPublicPath) { if (authState.needsRoleSelection) return '/role-selection'; if (authState.needsClassCode) return '/class-code'; return '/home'; } // 已认证 + 访问受保护页面 → 检查是否需要额外步骤 if (isAuthenticated) { if (authState.needsRoleSelection && currentPath != '/role-selection') { return '/role-selection'; } if (authState.needsClassCode && currentPath != '/class-code' && currentPath != '/role-selection') { return '/class-code'; } return null; } // 未认证 + 访问公开页面 → 放行 if (isPublicPath) return null; // 未认证 + 访问受保护页面 → 重定向到登录 return '/login'; }, // 监听认证状态变化,自动触发重定向 refreshListenable: _AuthListenable(authBloc), routes: [ // 启动页 & 引导页(无 Shell,无需认证) GoRoute( path: '/splash', name: 'splash', builder: (context, state) => const SplashPage(), ), GoRoute( path: '/onboarding', name: 'onboarding', builder: (context, state) => const OnboardingPage(), ), // 认证路由(无 Shell) GoRoute( path: '/login', name: 'login', builder: (context, state) => const LoginPage(), ), GoRoute( path: '/role-selection', name: 'roleSelection', builder: (context, state) => const RoleSelectionPage(), ), GoRoute( path: '/class-code', name: 'classCode', builder: (context, state) => const ClassCodeJoinPage(), ), // 主 Shell 路由(底部导航: 首页/日历/发现/我的) ShellRoute( navigatorKey: _shellNavigatorKey, builder: (context, state, child) { final index = _selectedIndexFromLocation(state.uri.path); return _AppShell( selectedIndex: index, child: child, ); }, routes: [ GoRoute( path: '/home', name: 'home', builder: (context, state) => const HomePage(), ), GoRoute( path: '/calendar', name: 'calendar', builder: (context, state) => const CalendarPage(), ), // 发现页(搜索页 — 标签+心情筛选日记) GoRoute( path: '/discover', name: 'discover', builder: (context, state) { final journalRepo = context.read(); return BlocProvider( create: (_) => SearchBloc(journalRepository: journalRepo), child: const SearchPage(), ); }, ), GoRoute( path: '/profile', name: 'profile', builder: (context, state) => const ProfilePage(), ), ], ), // 全屏页面(无底部导航) GoRoute( path: '/editor', name: 'editor', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) { final journalId = state.uri.queryParameters['id']; final templateId = state.uri.queryParameters['template']; return EditorPage(journalId: journalId, templateId: templateId); }, ), // 心情追踪(全屏,从首页心情卡片进入) GoRoute( path: '/mood', name: 'mood', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const MoodPage(), ), // 周概览(全屏,从日历页进入) GoRoute( path: '/weekly', name: 'weekly', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const WeeklyPage(), ), // 月度概览(全屏,从日历页进入) GoRoute( path: '/monthly', name: 'monthly', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const MonthlyPage(), ), GoRoute( path: '/class', name: 'class', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const ClassPage(), ), GoRoute( path: '/teacher', name: 'teacher', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const TeacherPage(), ), GoRoute( path: '/parent', name: 'parent', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) { return BlocProvider( create: (_) => ParentBloc(api: context.read()), child: const ParentPage(), ); }, ), GoRoute( path: '/achievements', name: 'achievements', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const AchievementPage(), ), GoRoute( path: '/stickers', name: 'stickers', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const StickerLibraryPage(), ), GoRoute( path: '/templates', name: 'templates', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const TemplateGalleryPage(), ), GoRoute( path: '/settings', name: 'settings', parentNavigatorKey: _rootNavigatorKey, builder: (context, state) => const SettingsPage(), ), ], ); } /// 路径 → Tab index 映射(4 项: 首页=0, 日历=1, 发现=2, 我的=3) int _selectedIndexFromLocation(String location) { if (location.startsWith('/calendar')) return 1; if (location.startsWith('/discover')) return 2; if (location.startsWith('/profile')) return 3; return 0; // /home 或未知路径 } /// AuthBloc 变化监听器 — 驱动 GoRouter refreshListenable class _AuthListenable extends ChangeNotifier { _AuthListenable(AuthBloc authBloc) { _subscription = authBloc.stream.listen((_) { notifyListeners(); }); } late final StreamSubscription _subscription; @override void dispose() { _subscription.cancel(); super.dispose(); } } /// App Shell — 包裹 ResponsiveScaffold /// TabBar: 首页(0) / 日历(1) / 发现(2) / 我的(3) /// 中心 FAB: 写日记 class _AppShell extends StatelessWidget { const _AppShell({ required this.selectedIndex, required this.child, }); final int selectedIndex; final Widget child; @override Widget build(BuildContext context) { return ResponsiveScaffold( selectedIndex: selectedIndex, onDestinationSelected: (index) { switch (index) { case 0: context.go('/home'); case 1: context.go('/calendar'); case 2: context.go('/discover'); case 3: context.go('/profile'); } }, body: child, onCenterButtonPressed: () => context.push('/editor'), ); } }