105 lines
3.5 KiB
Markdown
105 lines
3.5 KiB
Markdown
# 【Demo】路由首页:三种跳转姿势
|
||
|
||
> 源文件:`lib/pages/demo/router_home_page.dart` | 分类:路由
|
||
> 一句话:push 传参 / await 接返回值 / pushNamed 命名路由 —— 跳转的三个基本姿势。
|
||
|
||
---
|
||
|
||
## 一、这个 Demo 解决什么问题
|
||
|
||
页面跳转是 App 的基本操作。本页演示三种姿势及其适用场景:
|
||
1. **push + 构造函数传参**:参数少、类型安全,最常用;
|
||
2. **push 并等待返回结果**:从"选择器页"带值回来(选性别等);
|
||
3. **pushNamed 命名路由 + arguments**:按路由名跳转,参数以 Map 传递。
|
||
|
||
> 核心思想:**页面跳转是"入栈/出栈"**(Navigator 是个栈)。
|
||
> push = 压栈,pop = 出栈;pop 时带值 → push 的 await 拿到值。
|
||
|
||
---
|
||
|
||
## 二、怎么运行 / 演示内容
|
||
|
||
```dart
|
||
runApp(const RouterApp());
|
||
```
|
||
|
||
- 方式 1:跳详情页(张三,18);
|
||
- 方式 2:跳选择器页,选"男/女" → 返回首页弹 SnackBar 显示选择结果;
|
||
- 方式 3:跳详情页(李四,25,走命名路由)。
|
||
|
||
---
|
||
|
||
## 三、核心知识点(新手向)
|
||
|
||
### 1. 方式 1:push + 构造函数传参(类型安全,首选)
|
||
```dart
|
||
Navigator.push(
|
||
context,
|
||
MaterialPageRoute(
|
||
builder: (_) => const DetailPage(name: '张三', age: 18),
|
||
),
|
||
);
|
||
```
|
||
- 参数是 Dart 强类型:写错类型编译期就报错;
|
||
- 页面构造写在 `builder` 里(路由是"惰性构建",进入页面才执行)。
|
||
|
||
### 2. 方式 2:await push 接收 pop 返回值
|
||
```dart
|
||
onPressed: () async {
|
||
// 注意:push 返回的是 Future,用 await 接收 pop 带回来的值
|
||
final result = await Navigator.push<String>(
|
||
context,
|
||
MaterialPageRoute(builder: (_) => const PickerPage()),
|
||
);
|
||
if (context.mounted) { // await 之后必须判 mounted!
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text('你选择了:${result ?? '未选择'}')),
|
||
);
|
||
}
|
||
},
|
||
```
|
||
- 泛型 `<String>` 声明返回类型;
|
||
- 用户点系统返回键(没有 pop 带值)→ result 为 null,`?? '未选择'` 兜底;
|
||
- **await 后 context.mounted 必须判**(页面可能已被销毁)。
|
||
|
||
### 3. 方式 3:pushNamed 命名路由 + arguments
|
||
```dart
|
||
Navigator.pushNamed(
|
||
context,
|
||
'/detail', // 必须在 MaterialApp.routes 里注册过
|
||
arguments: {'name': '李四', 'age': 25},
|
||
);
|
||
```
|
||
- arguments 是 `dynamic`(任意类型),取值需判空强转(见 [detail_page.md](detail_page.md));
|
||
- 适合路由名集中管理的场景,代价是**丢失了类型安全**。
|
||
|
||
---
|
||
|
||
## 四、关键代码剖析
|
||
|
||
```dart
|
||
// 页面底部常驻的知识点提示(学习型 Demo 的惯例)
|
||
Text(
|
||
'知识点:页面跳转是【入栈/出栈】。'
|
||
'返回页 pop 时带值 → 接收页 await push 拿到值。'
|
||
'命名路由的 arguments 是 dynamic,取值需判空强转。',
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 五、踩坑记录
|
||
|
||
1. **`await` 之后用 `context` 必须先判 `context.mounted`**(Flutter 3.7+ 的推荐写法,替代旧式 `if (!mounted) return`);
|
||
2. `pushNamed` 没在 routes 注册 → 运行期 `onUnknownRoute` 报错/黑屏;
|
||
3. 忘了 `await push` 就收不到 pop 返回值;
|
||
4. 返回结果用泛型约束:`Navigator.push<String>`,别让类型随意流转。
|
||
|
||
---
|
||
|
||
## 六、扩展思考
|
||
|
||
- pop 带值的一方见 [picker_page.md](picker_page.md)(`Navigator.pop(context, '男')`);
|
||
- 参数接收的两种通道见 [detail_page.md](detail_page.md);
|
||
- 真实项目推荐 go_router:声明式路由 + 守卫 + 深链,但底层仍是 Navigator 这套入栈/出栈机制。
|