Flutter导航Navigator使用详解 您所在的位置:网站首页 navigator客车 Flutter导航Navigator使用详解

Flutter导航Navigator使用详解

#Flutter导航Navigator使用详解| 来源: 网络整理| 查看: 265

Flutter中提供了Navigator实现页面跳转功能,一个独立页面就是一个路由,因此称为路由导航。

有两种跳转方式: 通过路由直接跳转,就是说想要跳转到Page,那么直接将Page当作参数传递进去就可以了(类似于安卓的intent直接跳转Activity)。 通过命名路由参数跳转,就是说先将PageA注册到路由表内并起一个名字,然后其他页面通过这个名字进行跳转(类似于安卓的隐式跳转)。 Navigator API方法列表

通过路由直接跳转:

push 栈顶入栈一个新的路由 pushReplacement 将当前路由替换为一个新的路由 pushAndRemoveUntil 根据参数决定栈内路由清除多少,并入栈一个新路由。

通过命名路由跳转:

pushNamed 栈顶入栈一个新的路由 pushReplacementNamed 将当前路由替换为一个新的路由 pushNamedAndRemoveUntil 根据参数决定栈内路由清除多少,并入栈一个新路由

路由主动出栈:

pop 当前路由出栈 popAndPushNamed 当前路由出栈,入栈一个新路由 popUntil 从栈顶开始,逐个pop,直到参数中指定的路由为止 canPop 判断当前路由是否可以出栈,如果栈内只有当前路由,返回false,如果当前路由前面还有路由,返回true。也就是说栈底的路由,该方法返回false maybePop 当前路由如能能出栈就出栈,如果不能就什么都不做。

路由删除:

removeRoute removeRouteBelow

路由替换:

replace replaceRouteBelow

路由判断方法:

of 获取当前context 的Navigator的实例. 几乎所有的Navigtor方法实现都是调用of后,再调用具体方法的,如pop: static bool pop(BuildContext context, [ T result ]) { return Navigator.of(context).pop(result); } 复制代码 通过路由直接跳转 Navigator.push(BuildContext context, Route route)

描述:当前页面不变,跳转到一个新的页面。也就是栈顶入栈一个新的路由。 路由关系:

当前路由顺序为A-B-C 从C push D 路由顺序为A-B-C-D 如果在D页面,pop了,那么路由顺序为A-B-C

页面间传递参数:

通过目标页面的构造方法传递参数 可以从目标页面接收回传的参数

示例:页面跳转,传递参数给下一个页面,并获取下一个页面关闭后返回给当前页面的参数。 当前页面:

RaisedButton( onPressed: () { // 接收一个Future的返回值,返回值是一个数组 var result = Navigator.push(context, MaterialPageRoute( builder: (BuildContext context){ return PageA("这是传递给PageA的参数");// 通过构造方法传递参数 } )); // 获取返回值 result.then((response){ this.setState((){ text = response[0]; }); }); }, child: Text("页面跳转至PageA,传递参数,并接收页面返回值"), ), 复制代码

PageA:

RaisedButton( child: Text("向上个页面回传值"), onPressed: (){ Navigator.pop(context,["来自PageA的回传参数"]);// 往回传一个数组 // Navigator.pop(context,"来自PageA的回传参数");// 往回传一个字符串 }, ) 复制代码 Navigator.pushReplacement(BuildContext context, Route route, { TO result })

路由关系:

当前路由顺序为A-B-C 从C pushReplacement D 路由顺序为A-B-D 如果在D页面pop了,那么路由顺序为A-B

参数关系:

通过目标页面的构造方法传递参数 页面已经不存在,无法从目标页面接收回传的参数 RaisedButton( onPressed: () async { // 接收一个Future的返回值,返回值是一个数组 var result = Navigator.pushReplacement(context, MaterialPageRoute( builder: (BuildContext context){ return PageA("这是传递给PageA的参数"); } )); // 获取返回值 result.then((response){ print("response===$response"); // 页面已经出栈,不可再次调用 // this.setState((){ // text = response[0]; // }); }); }, child: Text("当前页面替换为PageA,可以传递参数,但无法接收页面返回值"), 复制代码 Navigator.pushAndRemoveUntil((BuildContext context, Route newRoute, RoutePredicate predicate)

描述:打开一个新的页面,并根据predicate的值的情况来决定如何处理新页面之前的路由,predicate值得情况分三种:

如果给predicate传递一个方法,方法的返回值是false,那么删除新页面之前的所有路由。 当前路由顺序为A-B-C-D-E 从E pushAndRemoveUntil 页面F,并且predicate返回false 路由顺序为F,A—E 全部出栈了。 如果在F页面pop了,那么app回到桌面。 如果predicate传递一个方法,方法的返回值为true,那么新页面之前的路由保持不变。 当前路由顺序为A-B-C-D-E 从E pushAndRemoveUntil 页面F,并且predicate返回true 路由顺序为A-B-C-D-E-F,A—E 全部不变。 如果在F页面pop了,那么路由为:A-B-C-D-E。 如果predicate的值是ModalRoute.withName指定的一个命名路由名称,那么新页面和该指定的路由名称之间的路由全部删除。 路由关系: 当前路由顺序为A-B-C-D-E 从E pushAndRemoveUntil 页面F,并且predicate的值是ModalRoute.withName('C')。(此处有坑,后面再讲) 路由顺序为A-B-C-F,D 、E 出栈。 如果在F页面pop了,那么路由顺序为:A-B-C。

参数关系:

通过目标页面的构造方法传递参数 页面已经不存在,无法从目标页面接收回传的参数

示例,给predicate传入一个方法,返回值是false或true

RaisedButton( onPressed: () async { // 接收一个Future的返回值,返回值是一个数组 var result = Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (BuildContext context) { return PageA("这是传递给PageA的参数"); }), // predicate参数 (route) { print('route=$route}'); // return false;// PageA之前的路由全部删除 return true; // PageA之前的路由保持不变 }); // 获取返回值 result.then((response) { // 页面已经出栈,不可再次调用 // this.setState((){ // text = response[0]; // }); }); }, 复制代码

示例,给predicate参数传递ModalRoute.withName,实现步骤:

假设有T-A-B-C-D五个路由 在T页面push到A,需要设置settings: RouteSettings(name:"pagea"),相当于给A打了一个标签“pagea”,如下: Navigator.push(context, MaterialPageRoute(settings: RouteSettings(name:"pagea"),builder: (BuildContext context) { return PageA("这是传递给PageA的参数"); })); 复制代码 然后A-push-B-push-C,此时路由顺序为:T-A-B-C 在C页面 pushAndRemoveUntil 到D页面,同时指定ModalRoute.withName("pagea")。 Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (BuildContext context) { return PageD("这是传递给PageD的参数"); }), ModalRoute.withName('pagea') ); 复制代码 此时路由顺序为:T-A-D。 最重要的是settings: RouteSettings(name:"pagea") ,ModalRoute.withName(name)中的参数name,指的是注册的路由名字,也可以是RouteSetting设定的那么,如果既注册路由了又RouteSetting了,那么RouteSetting的优先。 通过命名路由跳转 首先需要将路由进行注册

在MaterialApp内注册路由

class MainPage extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Good'), routes: { "mainpage": (context) => MainPage(), "navigatortest": (context) => NavigatorTest(), "pagea": (context) => PageA("aaa"), "pageb": (context) => PageB("bbb"), "pagec": (context) => PageC("cc"), "paged": (context) => PageD("dd"), }, ); } } 复制代码 路由跳转 Navigator.pushNamed(context, "paged", arguments: "111");//跳转到paged,并传递参数 Navigator.pushReplacementNamed(context, "paged",arguments: "111");//用paged替换掉当前路由,并传递参数 Navigator.pushNamedAndRemoveUntil(context, "paged", (route) { // return true; return false; }, arguments: "111");//跳转到paged,并根据第三个参数predicate的返回值决定新路由之前的路由的处理方式,为true保持不变,为false则新路由外的所有路由都pop掉。 Navigator.pushNamed(BuildContext context, String routeName, {Object arguments})

描述:与push方法作用一样,从当前页面打开一个新的页面,栈顶入栈一个路由,当前路由不变。

路由关系:

当前路由顺序为A-B-C 从C push D 路由顺序为A-B-C-D 如果在D页面,pop了,那么路由顺序为A-B-C

参数传递:

在调用pushNamed方法时,有个可选命名参数arguments,通过此参数传递给下个页面数据。 下个页面在build方法内获取参数,通过ModalRoute.of(context).settings.arguments接收到参数。 pushNamed方法的返回值也是一个Future,可以接收下个页面返回的数据。 RaisedButton( onPressed: () { var result = Navigator.pushNamed(context, "pagea", arguments: "111"); result.then((response){ // 下个页面的返回值 }); }, child: Text("pushNamed 打开新页面,传递参数和接收返回值"), ), 复制代码

新路由接收参数 在build方法内获取参数,var args=ModalRoute.of(context).settings.arguments;

class _PageDState extends State { @override Widget build(BuildContext context) { // 接受参数 var args=ModalRoute.of(context).settings.arguments; return Scaffold( appBar: AppBar( title: Text("PageD"), centerTitle: true, ), body: Center(, ); } } 复制代码 Navigator.pushReplacementName()

Navigator.pushReplacementNamed 与Navigator.pushReplacement唯一区别是用注册的路由命名替换了new PageA()式的直接路由。其他使用方法是一样的

Navigator.pushNamedAndRemoveUntil()

Navigator.pushNamedAndRemoveUntil 与Navigator.pushAndRemoveUntil唯一区别是用注册的路由命名替换了new PageA()式的直接路由。其他使用方法是一样的

路由出栈 Navigator.pop(BuildContext context, [ T result ])

描述:关闭当前页面,并将参数传递给来时的页面。也就是,当前路由从栈顶出栈。

当前路由顺序为A-B-C 在C页面pop,那么路由为:A-B 在pop时可以向来时的路由页面传递参数。 RaisedButton( child: Text("向上个页面回传值"), onPressed: (){ Navigator.pop(context,result);// 任何类型的参数 }, ) 复制代码 bool canPop(BuildContext context)

描述:判断当前路由是否可以出栈,如果栈内只有当前路由,返回false,如果当前路由前面还有路由,返回true。也就是说栈底的路由,该方法返回false。

if(Navigator.canPop(context)){ Navigator.pop(context); } 复制代码

如果canPop返回false,但是执行了pop方法,那么就会报错。

Navigator.maybePop(BuildContext context, [ T result ])

描述: 当前路由如能能出栈就出栈,如果不能就什么都不做。

是pop的安全返回方式. 参数作用于pop的一样。 Navigator.maybePop(context); 复制代码 相当于对pop外层加了一层canpop判断,等同于: if(Navigator.canPop(context)){ Navigator.pop(context); } 复制代码 Navigator.popAndPushNamed(BuildContext context,String routeName, {TO result,Object arguments,})

描述:当前路由出栈,栈顶入栈一个新路由。

路由顺序:

当前路由顺序为:A-B-C 在C popAndPushNamed 路由D 那么路由顺序为: A-B-D RaisedButton( onPressed: () { Navigator.popAndPushNamed(context, "paged"); }, child: Text("popAndPushNamed"), ), 复制代码 Navigator.popUntil(BuildContext context, RoutePredicate predicate)

描述:栈顶路由逐个出栈,直至predicate指定的条件。 路由顺序:

当前路由顺序为A-B-C-D-E 在E popUntil(context,Module.withName('B')),withName内的名字可以是为路由注册的名字,或是为路由设置的RouteSetting。 那么路由顺序为:A-B RaisedButton( onPressed: () { Navigator.popUntil(context,ModalRoute.withName('pagea')); }, child: Text("popUntil"), ), 复制代码 路由删除和替换

路由的删除和替换,通常是对站内已经存在的路由的处理,因此,想要删除一个路由,需要拿到对该路由的引用。 建立一个Map保存那些已经存在的路由:

class RouteMap { static Map map = new Map(); // 保存路由 static addRoute(String key,MaterialPageRoute route){ map[key]=route; } // 获取指定的路由 static MaterialPageRoute getRoute(String key){ return map[key]; } } 复制代码

每次跳转到新的路由时,都将新的路由的引用保存在RouteMap中,如:

RaisedButton( onPressed: (){ MaterialPageRoute route = MaterialPageRoute(builder: (BuildContext context) { return PageA("这是传递给PageA的参数"); }); RouteMap.addRoute("pagea", route);// 保存路由 Navigator.push(context,route); }, child: Text("add Route"), ), 复制代码

我们假设路由的跳转顺序为:A-B-C-D-E-F 那么RouteMap中的值为:

RouteMap.addRoute("pageb", routeb); RouteMap.addRoute("pagec", routec); RouteMap.addRoute("paged", routed); RouteMap.addRoute("pagee", routee); RouteMap.addRoute("pagef", routef); 复制代码 Navigator.removeRoute(BuildContext context, Route route)

描述:删除一个指定的路由,如果该路由存在于栈内正常删除,否则报错 路由顺序:

假设当前路由顺序为:A-B-C-D-E-F 在F页面,Navigator.removeRoute(context,RouteMap.getRoute("paged")); 路由顺序为:A-B-C-E-F RaisedButton( onPressed: () { Navigator.removeRoute(context,RouteMap.getRoute("pagea")); }, child: Text("removeRoute"), ), 复制代码 Navigator.removeRouteBelow(BuildContext context, Route anchorRoute)

描述:给定一个路由,在路由表内删除其前一个路由

假设当前路由顺序为:A-B-C-D-E-F 在F页面,removeRouteBelow(context,RouteMap.getRoute("paged")); 路由顺序为:A-B-D-E-F RaisedButton( onPressed: () { Navigator.removeRouteBelow(context,RouteMap.getRoute("paged")); }, child: Text("removeRouteBelow"), ), 复制代码 Navigator.replace(BuildContext context, { @required Route oldRoute, @required Route newRoute })

描述:用一个新的路由,将路由表内的一个已存在的路由替换掉

假设当前路由顺序为:A-B-C-D-E 在E页面,replace(context,oldRoute:RouteMap.getRoute("pageb"),newRoute:MaterialPageRoute(PageF()); 路由顺序为:A-F-C-D-E RaisedButton( onPressed: () { Navigator.replace(context, oldRoute: RouteMap.getRoute("pageb"), newRoute: MaterialPageRoute(builder: (BuildContext context){ return PageF("dd"); })); }, child: Text("replace"), ), 复制代码 Navigator.replaceRouteBelow(BuildContext context, { @required Route anchorRoute, Route newRoute })

描述:用一个新的路由,将路由表内的一个已存在的路由的前面的路由给替换掉

假设当前路由顺序为:A-B-C-D-E 在E页面,replace(context,oldRoute:RouteMap.getRoute("pagec"),newRoute:MaterialPageRoute(PageF()); 路由顺序为:A-F-C-D-E RaisedButton( onPressed: () { Navigator.replaceRouteBelow(context, anchorRoute: RouteMap.getRoute("pagec"),newRoute: MaterialPageRoute(builder: (BuildContext context){ return PageF("dd"); })); }, child: Text("replaceRouteBelow"), ), 复制代码 通过路由导航与通过命名路由导航的异同

路由导航:

直接通过类名跳转,跳转动作分散在各个类的内部。 界面间传递数据必须放在目标类的构造方法内。 命名路由: 所有路由都放到一个地方进行注册,一目了然。 界面间传递数据是通过方法参数传递过去的。 如果在业务场景中必须实时创建一些类的路由,构造方法内的数据是业务关联的,那么命名路由就不太合适了。

因此根据自己的实用场景进行选择,最好不要混用,而是只选择一种使用,我个人倾向于使用命名路由,方便管理。

如果您对文中的观点有异议,欢迎留言。如果本文对您有些许帮助的话,麻烦点个赞,谢!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有