import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:im_app/features/chat/view/chat_db_test_page.dart'; import 'package:im_app/features/app_tab/view/app_tab.dart'; import 'package:im_app/features/chat/view/chat_detail_page.dart'; import 'package:im_app/features/chat/view/chat_page.dart'; import 'package:im_app/features/contact/view/contact_page.dart'; import 'package:im_app/features/login/view/login_page.dart'; import 'package:im_app/features/settings/view/settings_page.dart'; import 'package:im_app/features/settings/view/theme_view.dart'; import 'package:im_app/features/settings/view/edit_profile_page.dart'; import 'package:im_app/features/settings/view/blocklist_page.dart'; import 'package:im_app/features/settings/view/language_page.dart'; import 'package:im_app/features/settings/view/network_diagnostics_page.dart'; import 'package:im_app/features/settings/view/about_page.dart'; import 'package:im_app/app/di/app_providers.dart'; import 'package:im_app/app/router/app_route_name.dart'; import 'package:im_app/app/router/guards/auth_guard.dart'; /// 应用路由 Provider /// /// 路由结构: /// ``` /// StatefulShellRoute(底部导航栏持久容器) /// ├── /chat ChatPage /// ├── /contact ContactPage /// └── /settings SettingsPage /// /// ── 全屏页面(无底部导航栏,parentNavigatorKey = _rootKey)── /// /chat/detail ChatDetailPage(extra 传参) /// /chat/:id ChatDetailPage(路径参数) /// /settings/theme ThemeView /// /login LoginPage /// ``` /// /// ## Shell 内 vs Shell 外 /// /// Shell 内路由(Tab 根路由)始终显示底部导航栏。 /// Shell 外路由(详情页 / 子功能页)无底部导航栏,push 进入后有返回按钮。 /// 这与 iOS / Android 主流 IM App 的交互一致(会话详情、设置子页均全屏)。 /// /// ## parentNavigatorKey 的作用 /// /// go_router push 时,路由默认放到"最近的 Navigator 祖先"上。 /// 在 StatefulShellBranch 内 push,最近的 Navigator 是 Branch Navigator, /// 而不是 Root Navigator,Shell 不会被盖住,TabBar 仍然可见。 /// /// 设置 `parentNavigatorKey: _rootKey` 后,路由强制放到 Root Navigator, /// 盖住整个 Shell,TabBar 消失,表现为真正的全屏页面。 /// /// ## 登录守卫 /// /// [authGuard] 检查 [AuthNotifier.isLoggedIn],未登录时重定向到 /login。 /// 登录 / 退出后 [AuthNotifier.notifyListeners] 触发 [refreshListenable], /// go_router 自动重新执行 redirect,无需手动跳转。 /// /// ## Tab 状态保持 /// /// [StatefulShellRoute.indexedStack] 为每个 Tab 维护独立的 Navigator 栈, /// 切换 Tab 时页面状态(滚动位置、输入内容等)不丢失。 // Root Navigator Key:供全屏路由声明 parentNavigatorKey,确保覆盖整个 Shell final _rootKey = GlobalKey(); final routerProvider = Provider((ref) { final authNotifier = ref.read(authNotifierProvider); return GoRouter( // Root Navigator 的 Key,供全屏路由声明 parentNavigatorKey 使用, // 确保 push 时覆盖整个 Shell(包括 TabBar) navigatorKey: _rootKey, // 冷启动默认落地页;authGuard 会在进入前检查登录状态并按需重定向 initialLocation: AppRouteName.chat.path, // 在控制台打印每次路由变化,方便开发期间调试;上线前设为 false debugLogDiagnostics: true, // 监听 authNotifier 变化:登录 / 退出时自动触发 redirect 重新执行, // 无需在业务代码里手动 context.go,守卫统一负责跳转 refreshListenable: authNotifier, redirect: (context, state) => authGuard(authNotifier, state), routes: [ // ── Shell 内:底部导航栏始终可见 ───────────────────────────────────── StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return AppTab(navigationShell: navigationShell); }, branches: [ StatefulShellBranch( routes: [ GoRoute( path: AppRouteName.chat.path, builder: (context, state) => const ChatPage(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: AppRouteName.contact.path, builder: (context, state) => const ContactPage(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: AppRouteName.settings.path, builder: (context, state) => const SettingsPage(), ), ], ), ], ), // ── Shell 外:全屏页面,无底部导航栏 ───────────────────────────────── // parentNavigatorKey: _rootKey 确保路由覆盖 Shell,TabBar 消失 // // extra 传参:接收 ({String conversationId, String title}) GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.chatDBTest.path, builder: (context, state) { return const ChatDbTestPage(); }, ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.chatDetail.path, builder: (context, state) { final extra = state.extra as ({String conversationId, String title}); return ChatDetailPage( conversationId: extra.conversationId, title: extra.title, ); }, ), // 路径参数:id 内嵌在 URL 中(/chat/:id) GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.chatDetailById.path, builder: (context, state) { final id = state.pathParameters['id']!; return ChatDetailPage( conversationId: id, title: '路径参数详情', ); }, ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsTheme.path, builder: (context, state) => const ThemeView(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsEditProfile.path, builder: (context, state) => const EditProfilePage(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsBlocklist.path, builder: (context, state) => const BlocklistPage(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsLanguage.path, builder: (context, state) => const LanguagePage(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsNetworkDiagnostics.path, builder: (context, state) => const NetworkDiagnosticsPage(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.settingsAbout.path, builder: (context, state) => const AboutPage(), ), GoRoute( parentNavigatorKey: _rootKey, path: AppRouteName.login.path, builder: (context, state) => const LoginPage(), ), ], ); });