在 Flutter 中,路由管理和Android、iOS类似,都是用于管理界面之间的跳转。在 flutter 中,我们使用 Navigator
来实现跳转的功能
界面跳转
在 flutter 中,支持两种跳转方式,一种是直接跳转,一种是通过路由表跳转。下面分别介绍:
直接跳转
我们可以使用 Navigator.push
方法直接跳转到新的界面,还可以使用 Navigator.pop
回退到之前的界面。代码示例如下:
less
Navigator.push(context, MaterialPageRoute(builder: (context) {
return NewRoute();
}));
其中MaterialPageRoute
是 Material组件库提供的组件,它可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画。
路由表跳转
要实现路由表跳转,我们需要先在 MaterialApp
的 routes 中注册,代码示例如下:
scala
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
// 注册路由表
routes: {
'newRoute': (context) => NewRoute(),
},
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
然后通过 Navigator.pushNamed
方法来跳转界面,代码示例如下:
arduino
Navigator.pushNamed(context, 'newRoute');
界面之间传递参数
界面A传递数据到界面B
- 直接跳转
对于直接跳转的情况,我们把数据传入就可以了,代码示例如下:
scala
class NewRoute extends StatelessWidget {
// 增加标题数据
final String title;
NewRoute({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New route $title"),
),
body: Center(
child: Text("This is new route"),
),
);
}
}
Navigator.push(context, MaterialPageRoute(builder: (context) {
// 设置对应的数据
return NewRoute(title: "新的标题",);
}));
- 路由表跳转
对于路由表跳转,我们需要通过 ModalRoute.of(context)!.settings.arguments
来接收参数,代码示例如下:
scala
class NewRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 接收参数
final args = ModalRoute.of(context)!.settings.arguments as Map<String, String>;
final message = args['message'];
return Scaffold(
appBar: AppBar(
title: Text("New route $message"),
),
body: Center(
child: Text("This is new route"),
),
);
}
}
Navigator.pushNamed(context, 'newRoute', arguments: {"title": "新的标题"});
界面B返回数据给界面A
scala
// 设置返回值
Navigator.pop(context, "我是返回值")
class RouterTestRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () async {
// 打开`TipRoute`,并等待返回结果
var result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return TipRoute(
// 路由参数
text: "我是提示xxxx",
);
},
),
);
//输出`TipRoute`路由返回结果
print("路由返回值: $result");
},
child: Text("打开提示页"),
),
);
}
}
首页
默认情况下,MaterialApp 中的 home 属性用来设置首页的界面。
scala
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
如果你想让首页也支持路由表跳转,需要使用 initialRoute 属性,代码示例如下:
less
MaterialApp(
title: 'Flutter Demo',
initialRoute:"/", //名为"/"的路由作为应用的home(首页)
theme: ThemeData(
primarySwatch: Colors.blue,
),
//注册路由表
routes:{
"new_page":(context) => NewRoute(),
"/":(context) => MyHomePage(title: 'Flutter Demo Home Page'), //注册首页路由
}
);
其他路由方法
Navigator
除了基础的push
和pop
方法外,还提供了其他的方法,如下表所示:
方法名 | 介绍 | 路由关系示例 | 参数传递与接收 |
---|---|---|---|
pushReplacementNamed(BuildContext context, String routeName, {Object arguments}) |
使用注册的路由命名替换当前路由,从当前页面跳转到新页面,栈顶路由被替换。类似pushReplacement ,只是使用路由命名而非直接路由。 |
当前路由顺序为A-B-C,从C执行pushReplacementNamed 到D(假设D已注册路由名为"paged"),路由顺序变为A-B-D。若在D页面执行pop ,路由顺序变为A-B |
通过arguments 参数传递数据给新页面,新页面在build 方法内通过ModalRoute.of(context).settings.arguments 获取参数。由于原页面已被替换,无法接收原页面返回值 |
popAndPushNamed<T extends Object, TO extends Object>(BuildContext context, String routeName, {TO result, Object arguments}) |
当前路由出栈,同时栈顶入栈一个新路由(通过注册的路由名指定)。 | 当前路由顺序为A-B-C,在C页面执行popAndPushNamed 到D(假设D已注册路由名为"paged"),路由顺序变为A-B-D |
可通过arguments 参数传递数据给新页面,新页面在build 方法内通过ModalRoute.of(context).settings.arguments 获取参数。result 参数用于在出栈时向原页面传递数据(若有需要) |
pushReplacement(BuildContext context, Route route, {TO result}) |
用新路由替换当前路由,跳转到新页面,原页面被移除。 | 当前路由顺序为A-B-C,从C执行pushReplacement 到D,路由顺序变为A-B-D。若在D页面执行pop ,路由顺序变为A-B |
通过目标页面的构造方法传递参数。由于原页面已被替换,无法接收原页面返回值 |
pushNamedAndRemoveUntil(BuildContext context, String routeName, RoutePredicate predicate, {Object arguments}) |
跳转到注册路由名对应的新页面,并根据predicate 的返回值决定新路由之前的路由的处理方式。类似pushAndRemoveUntil ,只是使用路由命名而非直接路由。 |
1. 若predicate 返回false ,当前路由顺序为A-B-C-D-E,从E执行pushNamedAndRemoveUntil 到F(假设F已注册路由名为"paged"),路由顺序变为F,A-E全部出栈。 2. 若predicate 返回true ,路由顺序变为A-B-C-D-E-F。 3. 若predicate 为ModalRoute.withName 指定的命名路由名称(如"pagea"),假设当前路由顺序为T-A-B-C-D,从C执行pushNamedAndRemoveUntil 到D(假设D已注册路由名为"paged"),路由顺序变为T-A-D |
通过arguments 参数传递数据给新页面,新页面在build 方法内通过ModalRoute.of(context).settings.arguments 获取参数。由于原页面部分或全部被移除,无法接收原页面返回值 |
popUntil(BuildContext context, RoutePredicate predicate) |
从栈顶开始逐个出栈路由,直到满足predicate 指定的条件(条件可以是注册的路由名或RouteSetting 设置的名称)。 |
当前路由顺序为A-B-C-D-E,在E页面执行popUntil(context, ModalRoute.withName('B')) ,路由顺序变为A-B |
在出栈过程中,若需要传递数据给前一个页面(在满足条件停止出栈前的页面),可结合其他机制(如在相关页面的状态管理中处理) |
fluro
前文介绍的所有路由知识都是Flutter官方提供的基本知识。在实际的Flutter项目中,开发人员往往并不这么写,因为还有一种更为方便的操作,也就是使用Flutter提供给我们的第三方路由库------fluro。
关于 fluro 的使用,具体可以看 初识 fluro 路由管理