Flutter页面跳转总结

前言

Flutter(本文用的flutter是1.9.1版本)和许多其他移动应用一样也是使用栈来管理页面的,进入一个新页面就是一个入栈操作,而退出一个页面时就是一个出栈操作。

在Flutter中一个页面就是一个Route对象,而管理这里Route对象的就是Navigator,例如Navigator.push负责入栈Route对象,Navigator.pop负责出栈Route对象。

页面跳转相关API介绍

一、跳转到新页面

相关方法

  1. Future<T> push<T extends Object>(Route<T> route)
  2. Future<T> pushNamed<T extends Object>(String routeName, {Object arguments, })

先看个例子(从Page1跳转到Page2):

Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page2()));
或:
Navigator.of(context).pushNamed("/page2");

首先,对于Navigator的使用,从上面例子中可以看出是通过Navigator.of(context)获取到当前的NavigatorState对象(of方法返回的是一个NavigatorState对象),然后调用对应的push方法或pushNamed方法。

这里也可以省略of方法,直接将context参数写到pushpushNamed方法中,如Navigator.pushNamed(context, "/page2");本质上是一样的,来看看源码:

static Future<T> pushNamed<T extends Object>(
    BuildContext context,
    String routeName, {
    Object arguments,
   }) {
    return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
 }

可见,最终也是先调用Navigator.of(context)方法来获取到当前的NavigatorState对象,然后在调用对应的方法。

匿名路由

对于push方法,前面我们说了Navigator管理的Route对象,而一个页面就是一个Route,而且通过上面的API也可以看出,push的第一个参数是一个Route对象,因此这里我们需要将要跳转的页面包装成一个Route对象传给Navigator。这里我们用的是MaterialPageRoute,一个带有Material风格的路由(比如实现了一些Material风格的跳转动画效果等)。另外还有IOS风格的路由CupertinoPageRoute。如果想要实现自定义效果,可以使用PageRouteBuilder去加自己想要的效果。

命名路由

pushNamed方法我们只传了一个字符串,这里又是这么生成Route对象的呐?这就是Flutter路由的另外一个使用方式了。要直接使用名字做跳转需要我们先对路由进行命名:

void main() {
  runApp(MaterialApp(
    home: MyAppHome(), // becomes the route named '/'
    routes: <String, WidgetBuilder> {
      '/page1': (context) => Page1(),
      '/page2': (context) => Page2(),
      '/page3': (context) => Page3(),
    },
  ));
}

然后在页面跳转的时候我们只需要使用pushNamed传递一个路由名字即可完成跳转,系统会使用名字和对应的路由构造器自动为我们创建一个Route。

注意:

  1. 对于home指定的页面,系统会自动命名为/
  2. 这里的名字是可以重复的,每个名字对应的页面也不是唯一的,所以这里是多对多的情况。比如home指定的参数是page1:home: Page1();,下面又添加了一个page1:'/page1': (ontext) => Page1(),当程序启动后(没做跳转),路由历史栈里面Page1对应的名字是/而不是/page1

所以,这两个方法的区别就是:使用push进行页面跳转的时候我们不用提前进行命名,但是每次跳转的时候需要手动创建一个Route,而使用pushNamed进行页面跳转的时候只需要一个名字即可,但是需要提前命名。

另外,按照上面例子中的写法,push跳转时生成的Route是没有名字的(是null),如需指定名字,可以这样写:

 Navigator.of(context).push(MaterialPageRoute(
       builder: (context) => Page2(),
       settings: RouteSettings(name: "/page2"),
 ));

即在创建Route的时候除了指定builder参数之外,还可以传递一个settings,在settings中去指定名字。

参数传递

通过前面方法的签名我们看到pushNamed有两个参数,第二个是一个Object arguments,这个就是用来传递参数的,类型是Object,也就是说我们可以传递任意类型的参数。对于push方法需要传递参数的话有两种方式,第一种是在页面的构造函数中传递,如:

Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page2(title: "hello")));

另外一种方式是通过前面提到过的settings参数来传递,如:

 Navigator.of(context).push(MaterialPageRoute(
       builder: (context) => Page2(),
       settings: RouteSettings(name: "/page2", arguments: {"title": "hello"}),
 ));

这里的arguments同样是一个Object,也就是可以指定任意的参数类型。pushNamed最终也是将名字和参数封装到settings中的。

在新页面中,通过以下方式取出传递的参数:

@override
  Widget build(BuildContext context) {
    Object arguments = ModalRoute.of(context).settings.arguments;
    // TODO
  }

接收返回值

通过前面可以看到,pushpushNamed的返回值类型是一个Future<T>,这个就是前一个页面的返回值,如:

Navigator.of(context).pushNamed("/page2").then((value){
    // 这里处理返回值 
    print("return value=$value");
 })

至于上一个页面如何设置返回值后面讲解pop方法的时候再说。

二、退出当前页面并跳转到新页面

  1. Future<T> pushReplacement<T extends Object, TO extends Object>(Route<T> newRoute, { TO result })
  2. Future<T> pushReplacementNamed<T extends Object, TO extends Object>(String routeName, {TO result, Object arguments, })
  3. Future<T> popAndPushNamed<T extends Object, TO extends Object>(String routeName, {TO result, Object arguments, })

这三个方法的功能都是退出当前页面并进入新的页面。1和2的区别就不说了,和前面一样。12和3的区别是:12两个方法都是先进入新的页面,然后再退出当前页面,也就是当前页面的退出动画是看不见的;方法3是先退出当前页面,然后在进入新的页面,也就是当前页面的退出动画是看得见的。

三、清除历史页面并跳转到新页面

  1. Future<T> pushNamedAndRemoveUntil<T extends Object>(String newRouteName, RoutePredicate predicate, {Object arguments, })
  2. Future<T> pushAndRemoveUntil<T extends Object>(Route<T> newRoute, RoutePredicate predicate)

这里重点是第二个参数predicate。在跳转新页面的时候会对当前历史栈里的页面依次进行遍历,然后通过predicate回调给用户进行处理,如果predicate返回false就表示这个页面需要退出,直到历史遍历完或者predicate返回true为止。

比如:

// 这里第二个参数始终返回false,则会清除所有历史页面,该方法执行完成后只会存在page2一个页面
Navigator.of(context).pushNamedAndRemoveUntil("/page2", (route) => false);

// 清除/page2之上的所有页面
Navigator.of(context).pushNamedAndRemoveUntil("/page4", (route) {
     return route.settings.name == "/page2";
 });

// 清除除了根页面之外的所有历史页面
Navigator.of(context).pushNamedAndRemoveUntil("/page2", ModalRoute.withName("/"));

这里的ModalRoute.withName主要也是对名字进行判断:

static RoutePredicate withName(String name) {
    return (Route<dynamic> route) {
      return !route.willHandlePopInternally
          && route is ModalRoute
          && route.settings.name == name; // 判断名字是否是指定的名字
    };
  }

四、退出当前页面

  1. bool pop<T extends Object>([ T result ]):退出当前页面,result为要返回的参数;
  2. void popUntil(RoutePredicate predicate):退出历史栈中的页面,直到predicate返回true;

另外还有两个方法:

  1. bool canPop():检查当前页面是否可以返回;
  2. Future<bool> maybePop<T extends Object>([ T result ]) async:尝试进行返回(不一定成功);

自定义路由

这里给个简单的自定义实现:

  Navigator.of(context).push(
    PageRouteBuilder(
      transitionDuration: Duration(milliseconds: 300), 
      pageBuilder: (context, animation, secondaryAnimation) {
        return FadeTransition(
          opacity: animation,
          child: Page2(),
        );
      },
    ),
  );

具体请见官方文档Custom routes部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335