108 lines
3.9 KiB
Markdown
108 lines
3.9 KiB
Markdown
# 【Demo】选择器页:pop 带值返回
|
||
|
||
> 源文件:`lib/pages/demo/picker_page.dart` | 分类:路由
|
||
> 一句话:路由返回值的经典用法 —— 点"男/女",把结果带回上一个页面。
|
||
|
||
---
|
||
|
||
## 一、这个 Demo 解决什么问题
|
||
|
||
"选性别"这类**选择器页**:打开 → 点选项 → 关闭并把结果带回调用页。
|
||
本页是 34 行的最小完整例子,专门讲清 **pop 的第二个参数**。
|
||
|
||
```
|
||
首页 await push 选择器页 → 用户点"男" → Navigator.pop(context, '男')
|
||
→ 首页的 await 拿到 '男' → 弹 SnackBar 展示
|
||
```
|
||
|
||
> 对照 Vue:等价于 `this.$emit('select', '男')` 后关闭弹窗——Flutter 里返回值走 `pop` 的第二个参数。
|
||
|
||
---
|
||
|
||
## 二、怎么运行 / 演示内容
|
||
|
||
```dart
|
||
runApp(const RouterApp()); // 从首页"方式 2"按钮进入本页
|
||
```
|
||
|
||
首页点"选择性别" → 本页两个选项(男/女)→ 点任一 → 回到首页并弹 SnackBar 显示结果;
|
||
按系统返回键(不选)→ 首页提示"未选择"。
|
||
|
||
---
|
||
|
||
## 三、核心知识点(新手向)
|
||
|
||
### 1. pop 的第二个参数就是"返回值"
|
||
```dart
|
||
ListTile(
|
||
title: const Text('男'),
|
||
onTap: () => Navigator.pop(context, '男'), // 第二个参数:返回给上一页的值
|
||
),
|
||
```
|
||
|
||
### 2. 与 push 侧配对使用
|
||
```dart
|
||
// 接收侧(router_home_page.dart)
|
||
final result = await Navigator.push<String>(context, MaterialPageRoute(...));
|
||
// 返回侧(本页)
|
||
Navigator.pop(context, '男');
|
||
```
|
||
- `pop(context, value)` 的值类型与 `push<String>` 的泛型对应;
|
||
- **用户没选(系统返回键 / 手势返回)→ 没调 pop 带值 → 接收侧拿到 `null`**,所以接收侧要 `?? '未选择'` 兜底。
|
||
|
||
### 3. pop 之后,页面就销毁了
|
||
`Navigator.pop` 会把当前路由出栈并销毁。所以**pop 之后再不能用本页的 context**(页面没了)——这也是"await 之后判 mounted"问题的另一面。
|
||
|
||
---
|
||
|
||
## 四、关键代码剖析
|
||
|
||
```dart
|
||
class PickerPage extends StatelessWidget {
|
||
const PickerPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(title: const Text('选择性别')),
|
||
body: Column(
|
||
children: [
|
||
ListTile(
|
||
leading: const Icon(Icons.male, color: Color(0xFF3B82F6)),
|
||
title: const Text('男'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: () => Navigator.pop(context, '男'), // ← 带值返回
|
||
),
|
||
Divider(height: 1.h, indent: 16, endIndent: 16),
|
||
ListTile(
|
||
leading: const Icon(Icons.female, color: Color(0xFFF06292)),
|
||
title: const Text('女'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: () => Navigator.pop(context, '女'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
全程没有 `setState`、没有返回值声明——**StatelessWidget 就够了**,选择器页是最典型的"纯跳转 + 带值返回"场景。
|
||
|
||
---
|
||
|
||
## 五、踩坑记录
|
||
|
||
1. **pop 不带值 = 返回 null**:用系统返回键关页时不会触发任何 `pop(context, value)`,接收侧必须处理 null;
|
||
2. **类型要配对**:`push<String>` 对应 `pop(context, '男')`;如果你 push 没写泛型,pop 的值类型就是 dynamic,接收侧要强转;
|
||
3. pop 之后本页 context 已失效,别再 await 什么(本页没有异步,天然安全);
|
||
4. 更多返回值的真实场景:日期选择、城市选择、筛选条件——结构都和本页一致。
|
||
|
||
---
|
||
|
||
## 六、扩展思考
|
||
|
||
- 接收侧完整代码见 [router_home_page.md](router_home_page.md)(`await push` + `context.mounted` + `?? '未选择'`);
|
||
- 参数**传入**的两种通道见 [detail_page.md](detail_page.md)(构造参数 vs 命名路由 arguments);
|
||
- 进阶:`Navigator.pop` 可以返回任意类型(对象、Map),比如"选了一个城市"返回整个城市模型,接收侧依然按泛型拿。
|