目录
3.2 pushReplacementNamed = startActivity+finsh()
**首先:一个App可能有多个导航器Navigator
MaterialApp内置了一个导航器Navigator
1 简单页面跳转
Navigator.of(context).pop VS Navigator.pop(context)
Navigator.of(context).push VS Navigator.push(context)
Navigator也是一个Widget 下面就会出现多个导航器
------ Navigator.of(context,rootNavigator)可以指定使用哪个导航器
class InitPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
///直接返回了一个Navigator对象
return Navigator(
//初始路由的名字
initialRoute: 'pageOne',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case 'pageOne':
//pageOne的路由对应的页面
builder = (BuildContext ctx) => PageOne();
break;
case 'pageTwo':
//pageTwo的路由对应的页面
builder = (BuildContext ctx) => PageTwo(
onSignupComplete: () {
///PageTwo点击事件,跳转到DefaultPage页面
//注意这个Navigator.of操作的是MaterialApp的内置Navigator,后面会有说明
Navigator.of(context).pop();
},
);
break;
}
return MaterialPageRoute(builder: builder, settings: settings);
);
}
}
2.1 命名 Route 详解
import 'package:flutter/material.dart';
class FMMaterialAppVC extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
body: AAA(),
),
routes: {
'/bbb': (context) => BBB(),
'/ccc': (context) => CCC(),
'/ddd': (context) => DDD(),
},
);
}
}
Navigator.pushNamed(context, '/bbb');
//flutter pop回首页, 不管导航栈有多少路由
Navigator.popUntil(context, (route) => route.isFirst);
2.2 路由跳转方式之 Push 详解
无参数
Navigator.pushNamed(context, '/bbb');
有参数
final datas = {"data": ["1","2","3"]};
Navigator.pushNamed(context, '/bbb', arguments: datas);
2.3 Navigator.push
无参数
Navigator.push(
context,
MaterialPageRoute(
builder: (context){
return BBB();
}
);
有参数,name 可以用来给路由命名,arguments 用来传递参数
final datas = {"data": ["1","2","3"]};
Navigator.push(
context,
MaterialPageRoute(
builder: (context){
return BBB();
},
settings: RouteSettings(
name: '/bbb',
arguments: datas,
),
),
);
上面代码意思就是跳转到 BBB() 页面,并且给 BBB() 命名为 '/bbb',在路由堆栈中,读取到这个 route 时,route.setting.name 与该命名相同。
3 路由跳转方式之 Push 进阶
2 中是最为常用的页面进出栈,但是在某些场合需要有特殊的处理。
注意:写法大同小异,后续就不在赘述传参方式,均与2相同。
3.1 pushNamedAndRemoveUntil
例如我们页面从AAA->BBB->CCC->DDD,这样进行页面,但是我们希望 CCC 再使用过后就被销毁。使得页面层级变为 AAA-BBB-DDD。
Navigator.of(context).pushNamedAndRemoveUntil('/ddd', (route) => route.settings.name == '/bbb');
跳转到指定页面,并删除前边所有页面
Navigator.of(context).pushNamedAndRemoveUntil('/ddd', (route) => route.isCurrent)
// 等价于下面这行代码
//Navigator.of(context).pushNamedAndRemoveUntil('/ddd', (route) => route.settings.name == '/ddd');
3.2 pushReplacementNamed = startActivity+finsh()
先跳转再处理路径
例如我们页面从AAA->BBB->CCC->DDD,这样进行页面,但是我们希望 CCC 再使用过后就被销毁。使得页面层级变为 AAA-BBB-DDD。
Navigator.pushReplacementNamed(context, '/ddd');
3.3 popAndPushNamed
先 pop 一级,然后在 push 到对应页面,效果与3.2 相同
Navigator.popAndPushNamed(context, '/ddd');
4 路由跳转方式之 Pop 详解
Navigator.pop(context);
判断是否可以 pop
bool canpop = Navigator.canPop(context);
if (canpop) Navigator.pop(context);
先判断是否可以 pop,如果可以,在pop,相当于 上边代码
Navigator.maybePop(context);
4.1 Navigator.pop
无参数
Navigator.pop(context);
有参数
final data = {"data":["1","2","3"]};
Navigator.pop(context, data);
4.2 路由跳转方式之 Pop 进阶
4 中是最为常用的页面进出栈,但是在某些场合需要有特殊的处理。pop 进阶方法比较少,只有 popUntil (2.1)与 popAndPushNamed ,在3.3中讲过后者了,这里不重复叙述。
- 回到栈顶
Navigator.popUntil(context, (route) => route.isFirst);
- 回到指定页面
我们使用下方代码回到 BBB 页面 AAA-BBB-CCC-DDD -> AAA-BBB
Navigator.popUntil(context, (route) => route.settings.name == '/bbb');
5. 初始路由 initialRoute
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
// home: Scaffold(
// body: AAA(),
// ),
initialRoute: '/ccc',
routes: {
'/aaa': (context) => AAA(),
'/bbb': (context) => BBB(),
'/ccc': (context) => CCC(),
'/ddd': (context) => DDD(),
},
);
}
-
a. 更改 initialRoute 为 routes 表里命名过得路由。
-
b. 设置根路由
设置路由 '/' ,当初始路由异常时,会停留在根路由。 - c. 使用未知路由
-
Widget build(BuildContext context) { // TODO: implement build return MaterialApp( // home: Scaffold( // body: AAA(), // ), initialRoute: '/cc', routes: { '/aaa': (context) => AAA(), '/bbb': (context) => BBB(), '/ccc': (context) => CCC(), '/ddd': (context) => DDD(), }, onUnknownRoute: (setting){ print(setting); return MaterialPageRoute(builder: (context) => AAA()); }, ); }
6. 路由拦截 onGenerateRoute
class FMMaterialAppVC extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
// home: Scaffold(
// body: AAA(),
// ),
initialRoute: '/',
routes: {
'/': (context) => AAA(),
'/bbb': (context) => BBB(),
'/ccc': (context) => CCC(),
'/ddd': (context) => DDD(),
},
// onGenerateInitialRoutes: (string){
// return [
// MaterialPageRoute(builder: (context) => AAA()),
// ];
// },
onGenerateRoute: (setting){
print(setting);
return MaterialPageRoute(builder: (context) => CCC());
},
onUnknownRoute: (setting){
print(setting);
return MaterialPageRoute(builder: (context) => AAA());
},
);
}
}
应该有很多小伙伴跟我一样,onGenerateRoute 不执行,明明写了 onGenerateRoute 方法,但是却不响应。查了挺多博客,没有相关描述,然后去翻了官方文档和源码,终于找到问题。
onGenerateRoute 路由拦截不能与命名路由一起使用,否则会只执行命名路由,不在进行拦截。下面我们注释掉命名路由,然后通过路由拦截,自定义一套路由跳转,从而实现命名路由的功能。
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
initialRoute: '/',
// routes: {
// '/': (context) => AAA(),
// '/bbb': (context) => BBB(),
// '/ccc': (context) => CCC(),
// '/ddd': (context) => DDD(),
// },
// onGenerateInitialRoutes: (string){
// return [
// MaterialPageRoute(builder: (context) => AAA()),
// ];
// },
onGenerateRoute: (setting){
print(setting);
final isLogin = true;
final routes = {
'/': (context) => AAA(),
'/bbb': (context) => BBB(),
'/ccc': (context) => CCC(),
'/ddd': (context) => DDD(),
};
if (!isLogin) {
return MaterialPageRoute(builder: (context) => AAA());
}
return MaterialPageRoute(builder: routes[setting.name], settings: setting);
},
onUnknownRoute: (setting){
print(setting);
return MaterialPageRoute(builder: (context) => AAA());
},
);
}
7. 路由传值
8.1 路由正向传值以及反参接收
BBB 页面 push 到 CCC,并等待 CCC 回来时的反参。
class BBB extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('BBB'),
),
body: Center(
child: RaisedButton(
child: Text('点击前往CCC'),
onPressed: (){
var backValueFromDDD = Navigator.pushNamed(context, '/ccc',arguments: {'value': "我是BBB页面传过来的值"});
backValueFromDDD.then((value){
print("CCC 传回来反参了,${value}");
});
// Navigator.pushNamedAndRemoveUntil(context, '/ccc', (route) => route.isCurrent);
},
),
),
);
}
}
我们使用 then 属性来接收下一个页面 pop 回来带的参数。
8.2 路由接收正向传值以及反向传参
我们在 CCC 类中增加以下代码。
Widget build(BuildContext context) {
// TODO: implement build
var value = ModalRoute.of(context).settings.arguments;
print("BBB 页面带过来参数了,${value}");
return Scaffold(
appBar: AppBar(
title: Text('CCC'),
),
body: _listView(context),
);
}
ListTile(
title: Text("Navigator.of(context).pop 传参"),
onTap: (){
Navigator.of(context).pop({"value":"我是CCC页面带回来的值"});
},
),
8.3 其他写法
在 BBB 中可以尝试以下写法,可以达到相同效果。
class BBB extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('BBB'),
),
body: Center(
child: RaisedButton(
child: Text('点击前往CCC'),
onPressed: () async {
var backValueFromDDD = await Navigator.pushNamed(context, '/ccc',arguments: {'value': "我是BBB页面传过来的值"});
backValueFromDDD;
print(backValueFromDDD);
// Navigator.pushNamedAndRemoveUntil(context, '/ccc', (route) => route.isCurrent);
},
),
),
);
}
}