提示信息
# 路由与页面开发规范
> 本项目使用 `go_router 17.x` 作为核心路由引擎,支持声明式路由映射、深链接(Deep Linking)以及自定义 Scheme 跳转。路由统一由 `NavigationService` 管理,Tab 状态通过 `StatefulShellRoute.indexedStack` 保持,页面跳转日志由 `TalkerRouteObserver` 自动记录。
---
## 一、路由架构规范(强制)
| 规范 | 说明 |
| :--- | :--- |
| 路由常量集中管理 | 所有路径字符串定义在 `AppRoutes`,禁止硬编码路径 |
| 跳转必须通过 `NavigationService` | 禁止直接使用 `Navigator.push / pop` |
| Tab 状态保持 | Tab 页使用 `StatefulShellRoute.indexedStack` |
| 日志自动记录 | 集成 `TalkerRouteObserver`,无需手动埋点 |
| 路由守卫异步化 | `redirect` 回调使用 go_router 17.x 异步签名(见下方) |
| UI 风格一致性 | 所有页面必须遵循 **Premium Editorial** 规范(衬线体标题、胶囊按钮、毛玻璃 Toast) |
### 接入步骤
**文件分工:**
- `lib/routes/app_routes.dart` — 统一管理路由常量字符串
- `lib/routes/app_router.dart` — 定义 `GoRouter` 实例
**步骤 1:定义 GoRouter 实例**
```dart
// lib/routes/app_router.dart
// ✅ go_router 17.x + Riverpod 3.x 完整写法
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'app_router.g.dart';
@riverpod
GoRouter router(Ref ref) { // ← Riverpod 3.x: 通用 Ref,无独立 XxxRef 类型
return GoRouter(
initialLocation: AppRoutes.splash,
observers: [TalkerRouteObserver(talker)],
redirect: (BuildContext context, GoRouterState state) async {
// ← go_router 17.x: 必须加 async,否则编译报类型不匹配
final location = state.matchedLocation;
// 放行白名单,避免 redirect loop
if (location == AppRoutes.splash || location == AppRoutes.privacy) {
return null;
}
final agreed = AppStorage.getBool(StorageKeys.privacyAgreed) ?? false;
if (!agreed) return AppRoutes.privacy;
final token = AppStorage.getString(StorageKeys.accessToken);
if (token == null) return AppRoutes.login;
return null;
},
routes: [...],
);
}
```
**步骤 2:在 MaterialApp.router 中挂载**
```dart
MaterialApp.router(
routerConfig: ref.watch(routerProvider),
)
```
### go_router 17.x Breaking Change:redirect 必须异步
```dart
// ✅ 正确 — 必须加 async
redirect: (BuildContext context, GoRouterState state) async {
return null;
}
// ❌ 错误 — 同步签名在 17.x 编译报类型不匹配
redirect: (BuildContext context, GoRouterState state) {
return null;
}
```
---
## 二、核心跳转工具(JumpUtil)
`JumpUtil` 是对 `NavigationService` 的统一封装,所有业务跳转均通过此工具调用,禁止绕过直接使用底层 API。
文件路径:`lib/core/utils/jump_util.dart`
```dart
// 内部跳转
JumpUtil.to(context, '/user_profile?uid=123');
// 外部链接
JumpUtil.to(context, 'https://google.com');
// Scheme 唤起(自动转为内部跳转)
JumpUtil.to(context, 'tongban://user_profile?uid=123');
```
---
## 三、跳转链接格式规范
| 类型 | 格式 | 示例 |
| :--- | :--- | :--- |
| 内部路径 | `/path?params` | `/user_profile?uid=123` |
| 完整 Scheme | `tongban://path?params` | `tongban://mine` |
| 外部链接 | `http(s)://...` | `https://example.com` |
---
## 四、自定义 Scheme
项目支持 `tongban://` 协议,用于从外部(网页、短信、第三方 App)唤起并跳转指定页面。
- **iOS**: `ios/Runner/Info.plist` → `CFBundleURLSchemes`
- **Android**: `android/app/src/main/AndroidManifest.xml` → `intent-filter`
---
## 五、新增路由步骤
### 步骤 1:定义路由常量
```dart
// lib/routes/app_routes.dart
static const String myNewPage = '/my_new_page';
```
### 步骤 2:注册路由映射
```dart
// lib/routes/route_generator.dart
GoRoute(
path: AppRoutes.myNewPage,
builder: (context, state) {
final id = state.uri.queryParameters['id'];
return MyNewPage(id: id);
},
),
```
### 步骤 3:代码中跳转
```dart
// ✅ 通过 JumpUtil(内部封装了 NavigationService)
JumpUtil.to(context, AppRoutes.myNewPage);
// ❌ 禁止直接使用 Navigator
Navigator.push(context, MaterialPageRoute(...)); // 禁止
```
### 步骤 4:后端下发
```json
{ "jump_url": "/my_new_page?id=abc" }
```
---
## 六、全页面路由总览
> 整理范围:APP 中所有独立展示的页面(Screen)。排除弹窗(Dialog)、底部菜单(ActionSheet)及非独立文档页。
### 6.1 核心导航与入口
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 1 | **应用主外壳** | — | 不参与路由,作为 `StatefulShellRoute` 外壳管理底部 Tab Bar | `lib/main/main_shell_page.dart` |
| 2 | **启动页** | `/splash` | 仅冷启动自动进入,执行隐私检查 / 配置加载 / 广告 / 登录校验,不支持外部跳转 | `lib/splash/splash_page.dart` |
### 6.2 主 Tab 页(底部导航栏)
通过 `StatefulShellRoute.indexedStack` 管理,各 Tab 状态独立保持。
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 3 | **首页** | `/home` | ✅ 支持直接跳转,含动态 / 信号列表、日志入口、通知入口 | `lib/main/tabs/home_tab.dart` |
| 4 | **消息页** | `/messages` | ✅ 支持直接跳转,含私聊 / 系统通知列表(目前为缺省页) | `lib/main/tabs/messages_tab.dart` |
| 5 | **我的页** | `/profile` | ✅ 支持直接跳转,含头像、统计、设置入口、退出登录 | `lib/main/tabs/profile_tab.dart` |
### 6.3 用户认证与隐私
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 6 | **登录 / 注册页** | `/login` | 支持跳转,未登录时由 `redirect` 自动重定向 | `lib/auth/login_page.dart` |
| 7 | **隐私协议确认页** | `/privacy` | 首次启动时由 `redirect` 强制跳转,用户同意后放行 | `lib/privacy/privacy_page.dart` |
### 6.4 社交 / 互动页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 8 | **聊天页** | `/chat` | ⚠️ 依赖复杂对象,仅支持 `extra` 传参,暂不支持纯 URL 跳转 | `lib/features/chat/chat_page.dart` |
| 9 | **用户主页** | `/user_profile` | ✅ 支持 URL 跳转,参数:`uid`(用户ID)、`source`(来源) | `lib/features/profile/user_profile_page.dart` |
| 10 | **用户列表页** | `/user_list` | ✅ 支持 URL 跳转,参数:`title`(标题)、`type`(fans/following) | `lib/features/user/user_list_page.dart` |
| 11 | **全部关注页** | `/following` | ✅ 支持直接跳转(复用 UserListPage) | `lib/features/user/user_list_page.dart` |
| 12 | **我的粉丝页** | `/fans` | ✅ 支持直接跳转(复用 UserListPage) | `lib/features/user/user_list_page.dart` |
| 13 | **黑名单页** | `/block_list` | ✅ 支持直接跳转(复用 UserListPage) | `lib/features/user/user_list_page.dart` |
| 14 | **访客页** | `/visitors` | ✅ 支持直接跳转,无需参数 | `lib/features/user/visitors_page.dart` |
| 15 | **举报页** | `/report` | ✅ 支持直接跳转 | `lib/features/common/report_page.dart` |
### 6.5 信号(Signal)页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 16 | **信号收件箱** | `/signal_inbox` | ✅ 支持直接跳转,无需参数 | `lib/features/signal/signal_inbox_page.dart` |
| 17 | **信号详情页** | `/signal_detail` | ⚠️ 建议通过 `extra` 传入 Signal 对象 | `lib/features/signal/signal_detail_page.dart` |
| 18 | **发送信号页** | `/send_signal` | ✅ 支持直接跳转 | `lib/features/signal/send_signal_page.dart` |
### 6.6 系统通知 / 公告页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 19 | **系统通知页** | `/system_notice` | ✅ 支持直接跳转,无需参数 | `lib/features/common/notices/system_notice_page.dart` |
### 6.7 个人资料编辑页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 20 | **编辑资料页** | `/profile_edit` | ✅ 支持直接跳转,登录态保护 | `lib/features/user/profile_edit_page.dart` |
| 21 | **MBTI 测试页** | `/mbti_test` | ✅ 支持直接跳转 | `lib/features/user/profile_edit/mbti_test_page.dart` |
### 6.8 聊天设置页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 22 | **聊天设置页** | `/chat_settings` | ⚠️ 需配合会话上下文,建议 `extra` 传参 | `lib/features/chat/chat_settings_page.dart` |
### 6.9 设置中心页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 23 | **设置页** | `/settings` | ✅ 支持直接跳转,无需参数 | `lib/features/settings/settings_page.dart` |
| 24 | **账号与安全页** | `/account_security` | ✅ 支持直接跳转,登录态保护 | `lib/features/settings/account_security_page.dart` |
| 25 | **隐私设置页** | `/privacy_settings` | ✅ 支持直接跳转 | `lib/features/settings/privacy_settings_page.dart` |
| 26 | **未成年保护页** | `/minor_mode` | ✅ 支持直接跳转 | `lib/features/settings/minor_mode_page.dart` |
| 27 | **系统权限页** | `/system_permissions` | ✅ 支持直接跳转 | `lib/features/settings/system_permissions_page.dart` |
| 28 | **缓存管理页** | `/cache_management` | ✅ 支持直接跳转 | `lib/features/settings/cache_management_page.dart` |
| 29 | **页面与展示设置页** | `/display_settings` | ✅ 支持直接跳转,含主题配色 / 头像形状 / 深色模式 | `lib/settings/display_settings_page.dart` |
| 30 | **关于我们页** | `/about_us` | ✅ 支持直接跳转,参数:`type`(1/2) | `lib/features/settings/about_us_page.dart` |
| 31 | **帮助与反馈页** | `/help_feedback` | ✅ 支持直接跳转 | `lib/features/settings/help_center_page.dart` |
### 6.10 商城 / 会员 / 活动页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 32 | **商城页** | `/store` | ✅ 支持直接跳转,无需参数 | `lib/features/store/store_page.dart` |
| 33 | **VIP 中心页** | `/vip_center` | ✅ 支持直接跳转 | `lib/features/vip/vip_center_page.dart` |
| 34 | **邀请好友页** | `/invite` | ✅ 支持直接跳转 | `lib/features/activity/invite_page.dart` |
| 35 | **抖音活动页** | `/douyin_activity` | ✅ 支持直接跳转 | `lib/features/activity/douyin_activity_page.dart` |
### 6.11 系统辅助页
| # | 页面名称 | 路由路径 | 跳转说明 | 文件路径 |
|---|--------|--------|--------|--------|
| 36 | **封禁提示页** | `/banned` | 账号被封禁时由 `redirect` 拦截并强制跳转,不支持主动跳转 | `lib/error/banned_page.dart` |
| 37 | **组件展示页** | `/dev/showcase` | 仅 Debug 模式可见,用于预览内置 UI 组件 | `lib/dev/component_showcase_page.dart` |
---
## 七、跳转支持状态汇总
| 状态标识 | 含义 |
|--------|------|
| ✅ 支持直接跳转 | 可通过 `JumpUtil.to(context, '/path')` 或后端下发 URL 直接跳转 |
| ⚠️ 仅支持 extra 传参 | 依赖复杂对象,需通过 `context.go('/path', extra: obj)` 传递,不支持纯 URL |
| 不支持外部跳转 | 仅由系统自动触发(如启动页) |
### 暂不支持纯 URL 跳转的页面
| 页面 | 路径 | 原因 |
|-----|------|------|
| 聊天页 | `/chat` | 依赖 Chat/User 复杂对象 |
| 信号详情页 | `/signal_detail` | 依赖 Signal 完整数据模型 |
| 聊天设置页 | `/chat_settings` | 依赖当前会话上下文 |
| 封禁提示页 | `/banned` | 仅由 `redirect` 守卫触发,不允许主动跳转 |
| 组件展示页 | `/dev/showcase` | 仅 Debug 模式,不对外暴露 |
---
## 八、页面数量统计
| 类别 | 页面数量 |
|-----|--------|
| 核心导航与入口 | 2 |
| 主 Tab 页 | 3 |
| 用户认证与隐私 | 2 |
| 社交 / 互动页 | 8 |
| 信号页 | 3 |
| 系统通知页 | 1 |
| 个人资料编辑页 | 2 |
| 聊天设置页 | 1 |
| 设置中心页 | 9 |
| 商城 / 会员 / 活动页 | 4 |
| 系统辅助页 | 2 |
| **合计** | **37** |
---
## 备注
- 第 10–13 页(用户列表、关注、粉丝、黑名单)共用同一个 `UserListPage` / `BlackListPage` 组件,通过 `type` 参数区分业务场景。
- `AboutUsPage` 内含用户协议 / 隐私政策链接,以 WebView 或 Document 形式展示,不计为独立页面。
- `main_shell_page.dart` 作为 `StatefulShellRoute` 外壳不参与路由路径计数,不计入页面总数。
- `privacy_page.dart` 现已纳入路由管理(路径 `/privacy`),并在 `redirect` 白名单中放行,避免 redirect loop。
- `banned_page.dart` 和 `component_showcase_page.dart` 由路由守卫 / 构建模式控制可达性,不对业务代码暴露跳转入口。
- 所有需要登录态的页面,路由层统一在 `router` 的 `redirect` 中处理守卫(异步写法),禁止各页面自行处理未登录跳转逻辑。
- 禁止在业务代码中直接调用 `Navigator.push / pop`,一律通过 `JumpUtil` 或 `NavigationService`。