4. 命名路由:路由表注册、Navigator.pushNamed、路由参数传递
命名路由,说白了就是给每个页面起个名字。
你想想看,如果每次跳转都要 new 一个 MaterialPageRoute,代码里到处都是 import 语句,维护起来确实头疼。我个人习惯是把所有路由集中管理,就像给每个房间贴个门牌号,想进哪个房间直接喊名字就行。
4.1 路由表注册:集中管理你的页面
在 Flutter 里,命名路由的注册是在 MaterialApp 里完成的。我们来看一个最基础的例子:
MaterialApp(
title: '我的应用',
initialRoute: '/',
routes: {
'/': (context) => HomePage(),
'/login': (context) => LoginPage(),
'/profile': (context) => ProfilePage(),
'/settings': (context) => SettingsPage(),
},
)
嗯,这里要注意:initialRoute 指定了首页是哪个路由。如果不写,默认就是 '/'。
我在项目中遇到过一个问题:团队里有人把路由名写成了 'Login',另一个人写成了 '/login',结果跳转时死活找不到页面。所以后来我们统一规定:所有路由名必须以斜杠开头,就像文件路径一样。
'/user/profile'。这样既清晰又不容易出错。
4.2 Navigator.pushNamed:用名字跳转
注册好路由表之后,跳转就变得非常简单了:
// 跳转到登录页
Navigator.pushNamed(context, '/login');
// 跳转到个人中心
Navigator.pushNamed(context, '/profile');
是不是很清爽?不用再写那一大堆 MaterialPageRoute 了。
但这里有个坑——路由表里没注册的页面,你 pushNamed 会直接报错。我曾经在开发环境里忘了注册一个新页面,结果测试同学一点按钮就白屏了。所以我的建议是:
- 所有页面都在 routes 里注册
- 如果页面太多,可以抽成一个单独的文件管理
- 用
onGenerateRoute做兜底处理(后面章节会讲)
4.3 路由参数传递:给页面带点数据
光跳转还不够,很多时候我们需要传参数。比如从商品列表页跳到详情页,总得告诉详情页是哪个商品吧?
Flutter 的命名路由支持通过 arguments 传参:
// 跳转并传参
Navigator.pushNamed(
context,
'/product/detail',
arguments: {
'id': 123,
'name': 'Flutter实战',
},
);
在目标页面里,通过 ModalRoute.of(context)!.settings.arguments 来接收:
class ProductDetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取参数
final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
final productId = args['id'];
final productName = args['name'];
return Scaffold(
appBar: AppBar(title: Text(productName)),
body: Center(child: Text('商品ID: $productId')),
);
}
}
ModalRoute.of(context)!.settings.arguments 返回的是 Object? 类型,你必须手动转型。如果转型失败,运行时会直接崩溃。我曾经因为传参类型写错了,排查了半天才发现是 Map 和 List 搞混了。
4.4 参数传递的最佳实践
为了避免上面说的类型问题,我个人习惯定义一个专门的参数类:
class ProductArgs {
final int id;
final String name;
ProductArgs({required this.id, required this.name});
}
// 跳转时
Navigator.pushNamed(
context,
'/product/detail',
arguments: ProductArgs(id: 123, name: 'Flutter实战'),
);
// 接收时
final args = ModalRoute.of(context)!.settings.arguments as ProductArgs;
print(args.name); // 类型安全,不会出错
这样做的好处很明显:
- 类型安全,编译期就能发现问题
- 代码可读性强,一看就知道需要传什么参数
- 重构时方便,改一个地方就行
4.5 命名路由的局限性
说实话,命名路由虽然方便,但也不是万能的。我在实际项目中遇到过几个问题:
| 问题 | 说明 |
|---|---|
| 参数类型不灵活 | arguments 是 Object? 类型,需要手动转型 |
| 不支持复杂动画 | 命名路由默认使用 MaterialPageRoute,想自定义动画得用 onGenerateRoute |
| 路由名硬编码 | 字符串写死在代码里,改起来麻烦 |
那怎么办呢?我的做法是:简单页面用命名路由,复杂场景用 onGenerateRoute。后面章节我会详细讲 onGenerateRoute 的用法,这里先留个悬念。
- 命名路由 = 给页面起名字,集中管理
- pushNamed 跳转,arguments 传参
- 参数建议用类封装,保证类型安全
- 命名路由适合简单场景,复杂场景需要更灵活的方案
好了,命名路由这块就聊到这儿。下一章我们看看 onGenerateRoute 怎么玩,那才是真正灵活的路由方案。