热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

多个路由指向同一个页面_Flutter路由导航

我们通常会用屏(Screen)来称呼一个页面(Page),一个完整的App应该是有多个Page组成的。在之前的

7ccd92fc951ecbf3b208446e1aa71afe.png
我们通常会用屏(Screen)来称呼一个页面(Page),一个完整的App应该是有多个Page组成的。
在之前的案例(豆瓣)中,我们通过IndexedStack来管理了首页中的Page切换:
首页-书影音-小组-市集-我的
通过点击BottomNavigationBarItem来设置IndexedStack的index属性来切换
除了上面这种管理页面的方式,我们还需要实现其它功能的页面跳转:比如点击一个商品跳转到详情页,某个按钮跳转到发送朋友圈、微博的编辑页面。
这种页面的管理和导航,我们通常会使用路由进行统一管理。

一. 路由管理

1.1. 认识Flutter路由

路由的概念由来已久,包括网络路由后端路由,到现在广为流行的前端路由

  • 无论路由的概念如何应用,它的核心是一个路由映射表
  • 比如:名字 detail 映射到 DetailPage 页面等
  • 有了这个映射表之后,我们就可以方便的根据名字来完成路由的转发(在前端表现出来的就是页面跳转)

在Flutter中,路由管理主要有两个类:Route和Navigator

1.2. Route

Route:一个页面要想被路由统一管理,必须包装为一个Route

  • 官方的说法很清晰:An abstraction for an entry managed by a Navigator.

但是Route是一个抽象类,所以它是不能实例化的

  • 在上面有一段注释,让我们查看MaterialPageRoute来使用

/// See [MaterialPageRoute] for a route that replaces the
/// entire screen with a platform-adaptive transition.
abstract class Route {
}

事实上MaterialPageRoute并不是Route的直接子类:

  • MaterialPageRoute在不同的平台有不同的表现
  • 对Android平台,打开一个页面会从屏幕底部滑动到屏幕的顶部,关闭页面时从顶部滑动到底部消失
  • 对iOS平台,打开一个页面会从屏幕右侧滑动到屏幕的左侧,关闭页面时从左侧滑动到右侧消失
  • 当然,iOS平台我们也可以使用CupertinoPageRoute

MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route

1.3. Navigator

Navigator:管理所有的Route的Widget,通过一个Stack来进行管理的

  • 官方的说法也很清晰:A widget that manages a set of child widgets with a stack discipline.

那么我们开发中需要手动去常见一个Navigator吗?

  • 并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
  • 所以,我们在需要的时候,只需要直接使用即可

Navigator.of(context)

Navigator有几个最常见的方法:

// 路由跳转:传入一个路由对象
Future push(Route route)// 路由跳转:传入一个名称(命名路由)
Future pushNamed(String routeName, {Object arguments,})// 路由返回:可以传入一个参数
bool pop([ T result ])

二. 路由基本使用

1.1. 基本跳转

我们来实现一个最基本跳转:

  • 创建首页页面,中间添加一个按钮,点击按钮跳转到详情页面
  • 创建详情页面,中间添加一个按钮,点击按钮返回到首页页面

7c2d630b500f98391977978009f38532.png

核心的跳转代码如下(首页中代码):

// RaisedButton代码(只贴出核心代码)
RaisedButton(child: Text("打开详情页"),onPressed: () => _onPushTap(context),
),// 按钮点击执行的代码
_onPushTap(BuildContext context) {Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {return DetailPage();}));
}

核心的返回代码如下(详情页中代码):

// RaisedButton代码(只贴出核心代码)
RaisedButton(child: Text("返回首页"),onPressed: () => _onBackTap(context),
)
// 按钮点击执行的代码
_onBackTap(BuildContext context) {Navigator.of(context).pop();
}

1.2. 参数传递

在跳转过程中,我们通常可能会携带一些参数,比如

  • 首页跳到详情页,携带一条信息:a home message
  • 详情页返回首页,携带一条信息:a detail message

40abad27053d50d45f5b48da210bdc9d.png

首页跳转核心代码:

  • 在页面跳转时,会返回一个Future
  • 该Future会在详情页面调用pop时,回调对应的then函数,并且会携带结果

_onPushTap(BuildContext context) {// 1.跳转代码final future = Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {return DetailPage("a home message");}));// 2.获取结果future.then((res) {setState(() {_message = res;});});
}

详情页返回核心代码:

_onBackTap(BuildContext context) {Navigator.of(context).pop("a detail message");
}

1.3. 返回细节

但是这里有一个问题,如果用户是点击右上角的返回按钮,如何监听呢?

方法一:自定义返回的按钮(在详情页中修改Scaffold的appBar)

appBar: AppBar(title: Text("详情页"),leading: IconButton(icon: Icon(Icons.arrow_back),onPressed: () {Navigator.of(context).pop("a back detail message");},),
),

方法二:监听返回按钮的点击(给Scaffold包裹一个WillPopScope)

  • WillPopScope有一个onWillPop的回调函数,当我们点击返回按钮时会执行
  • 这个函数要求有一个Future的返回值:
    • true:那么系统会自动帮我们执行pop操作
    • false:系统不再执行pop操作,需要我们自己来执行

return WillPopScope(onWillPop: () {Navigator.of(context).pop("a back detail message");return Future.value(false);},child: Scaffold(appBar: AppBar(title: Text("详情页"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [RaisedButton(child: Text("返回首页"),onPressed: () => _onBackTap(context),),Text(_message, style: TextStyle(fontSize: 20, color: Colors.red),)],),),),
);

三. 命名路由使用

3.1. 基本跳转

我们可以通过创建一个新的Route,使用Navigator来导航到一个新的页面,但是如果在应用中很多地方都需要导航到同一个页面(比如在开发中,首页、推荐、分类页都可能会跳到详情页),那么就会存在很多重复的代码。

在这种情况下,我们可以使用命名路由(named route)

  • 命名路由是将名字和路由的映射关系,在一个地方进行统一的管理
  • 有了命名路由,我们可以通过Navigator.pushNamed() 方法来跳转到新的页面

命名路由在哪里管理呢?可以放在MaterialApp的 initialRouteroutes

  • initialRoute:设置应用程序从哪一个路由开始启动,设置了该属性,就不需要再设置home属性了
  • routes:定义名称和路由之间的映射关系,类型为Map

修改MaterialApp中的代码:

return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue, splashColor: Colors.transparent),initialRoute: "/",routes: {"/home": (ctx) => HYHomePage(),"/detail": (ctx) => HYDetailPage()},
);

修改跳转的代码:

_onPushTap(BuildContext context) {Navigator.of(context).pushNamed("/detail");
}

在开发中,为了让每个页面对应的routeName统一,我们通常会在每个页面中定义一个路由的常量来使用:

class HYHomePage extends StatefulWidget {static const String routeName = "/home";
}class HYDetailPage extends StatelessWidget {static const String routeName = "/detail";
}

修改MaterialApp中routes的key

initialRoute: HYHomePage.routeName,
routes: {HYHomePage.routeName: (ctx) => HYHomePage(),HYDetailPage.routeName: (ctx) => HYDetailPage()
},

3.2. 参数传递

因为通常命名路由,我们会在定义路由时,直接创建好对象,比如HYDetailPage()

那么,命名路由如果有参数需要传递呢?

pushNamed时,如何传递参数:

_onPushTap(BuildContext context) {Navigator.of(context).pushNamed(HYDetailPage.routeName, arguments: "a home message of naned route");
}

在HYDetailPage中,如何获取到参数呢?

  • 在build方法中ModalRoute.of(context)可以获取到传递的参数

Widget build(BuildContext context) {// 1.获取数据final message = ModalRoute.of(context).settings.arguments;}

3.3. 路由钩子

3.3.1. onGenerateRoute

加入我们有一个HYAboutPage,也希望在跳转时,传入对应的参数message,并且已经有一个对应的构造方法

在HYHomePage中添加跳转的代码:

RaisedButton(child: Text("打开关于页"),onPressed: () {Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message");},
)

HYAboutPage的代码:

class HYAboutPage extends StatelessWidget {static const String routeName = "/about";final String message;HYAboutPage(this.message);@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("关于页面"),),body: Center(child: Text(message, style: TextStyle(fontSize: 30, color: Colors.red),),),);}
}

但是我们继续使用routes中的映射关系,就不好进行配置了,因为HYAboutPage必须要求传入一个参数;

这个时候我们可以使用onGenerateRoute的钩子函数:

  • 当我们通过pushNamed进行跳转,但是对应的name没有在routes中有映射关系,那么就会执行onGenerateRoute钩子函数;
  • 我们可以在该函数中,手动创建对应的Route进行返回;
  • 该函数有一个参数RouteSettings,该类有两个常用的属性:
    • name: 跳转的路径名称
    • arguments:跳转时携带的参数

onGenerateRoute: (settings) {if (settings.name == "/about") {return MaterialPageRoute(builder: (ctx) {return HYAboutPage(settings.arguments);});}return null;
},

30e943a75da72fd0a14cfc890b10e193.png

3.3.2. onUnknownRoute

如果我们打开的一个路由名称是根本不存在,这个时候我们希望跳转到一个统一的错误页面。

比如下面的abc是不存在有对应的页面的

  • 如果没有进行特殊的处理,那么Flutter会报错。

RaisedButton(child: Text("打开未知页面"),onPressed: () {Navigator.of(context).pushNamed("/abc");},
)

我们可以创建一个错误的页面:

class UnknownPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("错误页面"),),body: Container(child: Center(child: Text("页面跳转错误"),),),);}
}

并且设置onUnknownRoute

onUnknownRoute: (settings) {return MaterialPageRoute(builder: (ctx) {return UnknownPage();});
},

备注:所有内容首发于公众号,之后除了Flutter也会更新其他技术文章,TypeScript、React、Node、uniapp、mpvue、数据结构与算法等等,也会更新一些自己的学习心得等,欢迎大家关注

dd2fe50a839cab1311ba7a5446dd89ed.png


推荐阅读
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 本文介绍了使用C++Builder实现获取USB优盘序列号的方法,包括相关的代码和说明。通过该方法,可以获取指定盘符的USB优盘序列号,并将其存放在缓冲中。该方法可以在Windows系统中有效地获取USB优盘序列号,并且适用于C++Builder开发环境。 ... [详细]
  • 使用这个技巧要达到的目标:一般来说,模型和控制器你都不会有相同的类名字。让我先创建一个取名为post的model。classPostextendsModel{}现在 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 本文介绍了协程的概念和意义,以及使用greenlet、yield、asyncio、async/await等技术实现协程编程的方法。同时还介绍了事件循环的作用和使用方法,以及如何使用await关键字和Task对象来实现异步编程。最后还提供了一些快速上手的示例代码。 ... [详细]
author-avatar
DCPe-苦乐年华
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有