Flutter 路由知识
[TOC]
1. Navigator
Navigator是一个路由管理的组件,它提供了打开和退出路由页方法。Navigator通过一个栈来管理活动路由集合。
Flutter分匿名路由与命名路由,比如直接使用Navigator.push就是匿名路由跳转, 使用Navigator.pushName就是使用命名路由跳转。
很多跳转方法只有命名路由才能用, 推荐使用命名路由。
路由表
使用命名路由, 必须要先提供并注册一个路由表, 这样应用程序才知道哪个名字与哪个路由组件相对应。其实注册路由表就是给路由定义个名字, 路由表定义如下:
Map<String, WidgetBuilder> routes;
typedef WidgetBuilder = Widget Function(BuildContext context);
这是一个Map, key值为路由的名字, 类型是String, value是个builder回调函数, 用于生成对应的路由widget。
在通过命名路由进行跳转的时候, 程序会根据路由名字在路由表中查找到对应的WidgetBuilder回调函数, 然后调用该回调函数生成路由widget并返回。
路由表的需要在MaterialApp下, 添加routes属性或者在onGenerateRoute参数中添加。
MaterialApp(
title: 'Flutter Demo',
initialRoute:"/", //名为"/"的路由作为应用的home(首页)
routes:{//注册路由表
'testPage':(context) => TestPage(),
'/':(context) => HomePage(),
} ,
);
命令路由传递参数
比如说Navigator.pushNamed(BuildContext context, String routeName, {Object arguments});
参数是arguments, 类型是Object , 一般我们使用的时候都是用Map<String, dynamic>
下面是使用方法
创建一个有参数的页面:
class ParamPage extends StatelessWidget {
final int index;
final List<String> list;
final TestParam testParam;
const ParamPage({Key key, this.index, this.list, this.testParam}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('有参数的路由',),),
body: Center(
child: OutlinedButton(
onPressed: (){
print(index);
print(list);
print(testParam.text);
},
child: Text('点击'),
),
),
);
}
}
class TestParam{
int i;
String text;
TestParam(this.i, this.text);
}
在路由表中定义:
MaterialApp(
title: 'Flutter Demo',
routes:{//注册路由表
'testPage':(context) => TestPage(),
'paramPage': (context){
final Map<String, dynamic> args = ModalRoute.of(context).settings.arguments;
return ParamPage(
index: args['index'],
list: args['list'],
testParam: args['testParam'],
);
}
} ,
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
也能在MaterialApp的onGenerateRoute参数中定义:
MaterialApp(
title: 'Flutter Demo',
onGenerateRoute: (RouteSettings settings) {
if(settings.name == 'paramPage'){
final Map<String, dynamic> args = settings.arguments as Map<String, dynamic>;
return ParamPage(
index: args['index'],
list: args['list'],
testParam: args['testParam'],
);
}
}
home: MyHomePage(title: 'DemoPage'),
);
打开命名路由页面, 并传递参数:
Navigator.pushNamed(
context,
'paramPage',
arguments: <String, dynamic>{
'index': 1,
'list': ['1', '2'],
'testParam': TestParam(1, 'text'),
},
);
路由相关方法
通常当前屏幕显示的页面就是栈顶的路由。Navigator提供了一系列方法来管理路由栈,下面整理列出常用的路由跳转相关方法。
-
push: 跳转到新页面。
-
pushNamed: 跳转到新页面, 需要路由名。
-
pop: 关闭当前页面, 返回到上个页面。
-
canPop: 判断是否可以导航到新页面。
-
maybePop: 可能会导航到新页面 ?( 暂时还不理解这个用法)。
-
pushReplacement: 替换当前路由。
-
pushReplacementNamed: 替换当前路由, 与上面一样, 需要路由名。
-
popAndPushNamed: 关闭当前页面, 再跳转到新页面。
-
pushAndRemoveUntil: 将给定路由推送到Navigator, 删除先前路由直至该方法的predicate参数返回true为止。
-
pushNamedAndRemoveUntil: 与上面👆🏻那个方法一样, 只不过替换成了命名路由。
-
popUntil: 反复执行pop, 直到该方法的predicate参数返回true为止。
使用方法
-
push
方法详情:
Navigator.push(BuildContext context, Route<T> route);此方法是将我们需要跳转的页面push到栈顶。参数context是上下文, 参数route我们使用MaterialPageRouter 或者 CupertinoPageRoute。
实例使用:
Navigator.push(context, MaterialPageRoute(builder: (context) => TestRoute2())); -
pushName
方法详情:
Navigator.pushNamed( BuildContext context, String routeName, {Object arguments} );此方法是将我们需要跳转的页面push到栈顶。routeName是定义好的路由名称。
arguments为需要传递的参数。
实例使用:
Navigator.pushNamed(context, 'routeName'); -
pushReplacement
方法详情:
Navigator.pushReplacement( BuildContext context, Route<T> newRoute, );此方法是把当前页面在栈中的位置替换成要跳转的路由, 当新页面进栈显示后, 之前的页面将执行dispose方法。
比如说1->2, 2到3的时候使用了pushReplacement, 在3按返回的时候是回到了1。
实例使用:
Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => TestRoute3()) ); -
pushReplacementNamed
方法详情:
Navigator.pushReplacementNamed( BuildContext context, String routeName, {Object arguments, Object result} );这个方法与上面👆🏻方法一样, 只不过跳转的路由换成路由名。
比如说1-->2, 2到3的时候使用了pushReplacementNamed, 在3按返回的时候是回到了1。
result为回调给页面1的参数, 在跳转页面1的方法后面加上.then((value){})就能接收到回调参数。
实例使用:
Navigator.pushReplacementNamed(context, 'routeName'); -
popAndPushNamed
方法详情:
Navigator.popAndPushNamed( BuildContext context, String routeName, {Object arguments, Object result} );这个方法与上面👆🏻方法一样, 区别是这个有pop关闭之前页面的动画, 关闭了这个页面再跳转到新页面。
比如说1-->2, 2到3的时候使用了popAndPushNamed, 在3按返回的时候是回到了1。
result为回调给页面1的参数, 在跳转页面1的方法后面加上.then((value){})就能接收到回调参数。
实例使用:
Navigator.popAndPushNamed( context, 'routeName', ); -
pushAndRemoveUntil
方法详情:
Navigator.pushAndRemoveUntil( BuildContext context, Route<T> newRoute, RoutePredicate predicate, );例如说一个购物支付例子, 可能会有首页1->挑选商品页2->填写订单3->支付确认页4->支付成功页5, 一旦用户完成了支付事件, 这次订单支付相关页面都应该从路由栈中删除, 在支付成功页5按返回应该回到首页1。
1-->2-->3-->4, 在4进入5时使用pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => TestRoute5()), ModalRoute.withName('testRoute1'));
这时候如果在页面5点击返回, 将会回到页面1。
实例使用:
Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => TestRoute5()), ModalRoute.withName('testRoute1'), ); -
pushNamedAndRemoveUntil
方法:
Navigator.pushNamedAndRemoveUntil( BuildContext context, String newRoute, RoutePredicate predicate, );与上面👆🏻方法作用相同, 不同之处是新路由换成了路由名, 路由名需要程序入口指定。
1-->2-->3-->4, 在4进入5时使用pushNamedAndRemoveUntil
这时候如果在页面5点击返回, 将会回到页面1。
实例使用:
Navigator.pushNamedAndRemoveUntil( context, 'testRoute5', ModalRoute.withName('testRoute1'), ); -
popUntil
方法详情:
Navigator.popUntil(BuildContext context, RoutePredicate predicate);例如说一个登录注册, 注册需要邀请码的例子, 从首页1跳转到登录页2, 输入账号发现账号未注册, 然后跳转到填写邀请码页面3, 在邀请码页面3填写完后需要直接回到首页, 在进入到邀请码页面3时又不能使用pushReplacement方法, 因为邀请码页面3还需要返回到登录页2, 这时候在邀请码页面3回到首页的时候可以使用
Navigator.popUntil(context, ModalRoute.withName('testRoute1'));这时候会直接回到首页1
1-->2-->3, 在页面3使用popUntil(context, ModalRoute.withName('testRoute1'), 会直接回到首页1
实例使用:
Navigator.popUntil( context, ModalRoute.withName('testRoute1'), );