路由在移动端中,通常指的是页面(page)。所谓路由管理,其实就是管理页面直接的跳转。而在Flutter中,我们使用Navigator来管理路由的跳转;使用PageRoute来描述我们要跳转到那个界面、转场的动画效果。
Navigator
Navigator
是一个路由管理的widget,它通过一个栈来管理一个路由widget集合。通常当前屏幕显示的页面就是栈顶的路由。下面我们通过学习Navigator
提供了一系列方法,来了解它是如何管理路由栈。
1. PushNamed
static Future<T> pushNamed<T extends Object>(
BuildContext context,
String routeName, {
Object arguments,
}) {
return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
根据路由名称(routeName),找寻对应的route,并实现见面的跳转。其中arguments
用来给跳转的route传值。
2. PushReplacementNamed
static Future<T> pushReplacementNamed<T extends Object, TO extends Object>(
BuildContext context,
String routeName, {
TO result,
Object arguments,
}) {
return Navigator.of(context).pushReplacementNamed<T, TO>(routeName, arguments: arguments, result: result);
}
PushReplacementNamed
和PushName
的作用都是根据路由名称(routeName),找寻对应的route,并实现见面的跳转。二者的区别在于PushReplacementNamed
会替换当前的路由,也就是说新push进入的route会替换原来route。
3. PopAndPushNamed
static Future<T> popAndPushNamed<T extends Object, TO extends Object>(
BuildContext context,
String routeName, {
TO result,
Object arguments,
}) {
return Navigator.of(context).popAndPushNamed<T, TO>(routeName, arguments: arguments, result: result);
}
PopAndPushNamed
和PushReplacementNamed
的作用类型,但是PopAndPushNamed
是先pop原来的route,再push新的route。
4. Push
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
Push
和PushName
的作用完全一样;二者的区别在于Push
直接跳转route,而PushName
需要根据routName
找寻相应的route后再实现跳转。
5. PushReplacement
static Future<T> pushReplacement<T extends Object, TO extends Object>(BuildContext context, Route<T> newRoute, { TO result }) {
return Navigator.of(context).pushReplacement<T, TO>(newRoute, result: result);
}
PushReplacement
和PushReplacementNamed
的作用完全一样;二者的区别在于PushReplacementNamed
需要根据routName
找寻相应的route,PushReplacement
不需要。
6. PushAndRemoveUntil
static Future<T> pushAndRemoveUntil<T extends Object>(BuildContext context, Route<T> newRoute, RoutePredicate predicate) {
return Navigator.of(context).pushAndRemoveUntil<T>(newRoute, predicate);
}
跳转到新的路由,并删除路由栈中的其他路由,直到predicate
为true后,停止删除。该方法的应用场景:可以应用于支付流程。在支付的过程中,我们会跳转多个界面(填写金额、选择支付方式、输入密码、支付结果...),当我们支付成功后,pop时不应该一层一层的,而应该直接返回根界面。
7. canPop 和 maybePop
static bool canPop(BuildContext context) {
final NavigatorState navigator = Navigator.of(context, nullOk: true);
return navigator != null && navigator.canPop();
}
static Future<bool> maybePop<T extends Object>(BuildContext context, [ T result ]) {
return Navigator.of(context).maybePop<T>(result);
}
canPop
判断当前route是否可以pop,返回bool;maybePop
先判断当前route是否可以pop,可以则直接pop,不行则没有任何效果。
8. pop 和 popUntil
static bool pop<T extends Object>(BuildContext context, [ T result ]) {
return Navigator.of(context).pop<T>(result);
}
static void popUntil(BuildContext context, RoutePredicate predicate) {
Navigator.of(context).popUntil(predicate);
}
pop
返回上一个route;popUntil
一直返回route,直到predicate
返回true时停止。
MaterialPageRoute
MaterialPageRoute
继承自PageRoute
类,PageRoute
类是一个抽象类,表示占有整个屏幕空间的一个模态路由页面,它还定义了路由构建及切换时过渡动画的相关接口及属性。MaterialPageRoute
是Material
组件库的一个Widget,它可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画:
- 对于Android,当打开新页面时,新的页面会从屏幕底部滑动到屏幕顶部;当关闭页面时,当前页面会从屏幕顶部滑动到屏幕底部后消失,同时上一个页面会显示到屏幕上。
- 对于iOS,当打开页面时,新的页面会从屏幕右侧边缘一致滑动到屏幕左边,直到新页面全部显示到屏幕上,而上一个页面则会从当前屏幕滑动到屏幕左侧而消失;当关闭页面时,正好相反,当前页面会从屏幕右侧滑出,同时上一个页面会从屏幕左侧滑入。
MaterialPageRoute({
@required this.builder, // 创建需要跳转的界面
RouteSettings settings, // 包含路由的配置信息,如路由名称、是否初始路由(首页)。
this.maintainState = true, // 默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainState为false。
bool fullscreenDialog = false, // 表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialog为true,新页面将会从屏幕底部滑入(而不是水平方向)。
}) : assert(builder != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
assert(opaque),
super(settings: settings, fullscreenDialog: fullscreenDialog);
注意:如果想自定义路由切换动画,可以自己继承PageRoute来实现,我们将在后面介绍动画时,实现一个自定义的路由Widget。
如何使用 Navigator 来管理 Route?
- Navigator 使用 push 将 route 添加到路由栈中
- Navigator 使用 pop 将 route 从路由栈中移除
/// Push
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return NewRoute();
}));
/// Pop
Navigator.pop(context);
命名路由
所谓命名路由(Named Route)即给路由起一个名字,然后可以通过路由名字直接打开新的路由。这为路由管理带来了一种直观、简单的方式。
要想使用命名路由,我们必须先提供并注册一个路由表(routing table),这样应用程序才知道哪个名称与哪个路由Widget对应。路由表的定义如下:
Map<String, WidgetBuilder> routes;
它是一个Map, key
为路由的名称,是个字符串;value是个builder回调函数,用于生成相应的路由Widget。我们在通过路由名称入栈新路由时,应用会根据路由名称在路由表中找到对应的WidgetBuilder回调函数,然后调用该回调函数生成路由widget并返回。
如何注册一个路由表
我们需要先注册路由表后,我们的Flutter应用才能正确处理命名路由的跳转。注册方式很简单,在我们的项目main.dart
文件中找到MaterialApp
,添加routes
属性,代码如下:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes: {
"new_page":(context)=>NewRoute(),
},
);
}
}
现在我们就完成了路由表的注册。
如何使用命名路由
通过上面的学习,我们知道可以使用Navigator
中pushNamed
来通过路由名称,完成路由跳转。
onPressed: () {
Navigator.pushNamed(context, "new_page");
},
如何给命名路由传递参数
// 注册路由
routes:{
"new_page":(context)=>EchoRoute(),
},
// 通过RouteSetting对象获取路由参数
class EchoRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
//获取路由参数
var args=ModalRoute.of(context).settings.arguments
//...省略无关代码
}
}
// Navigator 跳转路由
Navigator.of(context).pushNamed("new_page", arguments: "hi");