使用 Flutter Navigator2.0 最舒服的姿势

简介: 使用 Flutter Navigator2.0 最舒服的姿势

flutter 路由2.0 的文章不少,大都是讲理论居多,本文主要讲实战。目前来说实际开发中很少需要兼容 web,说了那么多有关 url 的解析有什么用?不考虑 web,就省了很多事。

在 Navigator 2.0 中,如果不考虑 web,只需要实现 RouterDelegate 就可以了。每次都实现一遍太没效率。我们可以从 RouterDelegate17 开始。RouterDelegate17 是 RouterDelegate 的子类,实现了页面的跳转,弹出,替换等功能,还能监控页面的状态。

RouterDelegate17 的使用

完整示例代码点 这里

示例演示


30751c2af478420f9e451b30c771dfda_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


注意看第一行是页面状态,当弹出对话框的时候,下面的页面状态变为 PageStatus.Leave,对话框关闭的时候,页面的状态变为 PageStatus.enter。

  1. 安装
flutter pub add router_delegate17
复制代码
  1. 增加两个页面 PageA,PageB。为了方便使用,增加一个全局变量 final routerDelegate = RouterDelegate17([const MaterialPage(child: PageA())]);
  2. 使用 routerDelegate
MaterialApp(
   title: 'flutter RouteDelegate17 demo',
      home: Router(
          routerDelegate: routerDelegate,
          backButtonDispatcher: RootBackButtonDispatcher()),
   );
复制代码
  1. 在 PageA 中 跳转 PageB
routeDelegate.push(MaterialPage(PageB());
复制代码
  1. PageB 中打开对话框
routeDelegate.openDialog ...其它省略,详见完整代码
复制代码

openDialog 就是系统 showDialog 的 包装,参数都一模一样,包装的目的,是为了可以让页面能更新状态。

看到这里,你会发现,routeDelegate17 实现的方法和 navigator1.0 的 方法很象,这样会降低使用成本。

所有的页面都在栈中。页面状态是 RouterDelegate17 实现的功能。RouterDelegate17 把页面分为三个状态

  • PageStatus.none 新页面刚入栈的时候
  • PageStatus.leave 顶层页面被其它页面遮挡的时候
  • Pagestatus.enter 被遮挡的页面重新成为顶层页面的时候

要获取页面状态非常容易,只需要获取 status 属性

var pageStatus = routerDelegate.currentNavSettings.status.value
复制代码

status 是一个 ValueNotifier,实际用的时候可以用 ValueListenableBuilder 监听变化

ValueListenableBuilder(
     valueListenable: routerDelegate.currentNavSettings.status,
     builder: (context, value, child) {
       return StatusText(text: value.toString());
 }),
复制代码
  1. 退出页面很容易

pop 可以带返回值

routerDelegate.pop(1)
复制代码

也可以直接 用

Navigator.of(context).pop(1)
复制代码
  1. 退出程序请求计数

当在首页试图弹出页面的时候,android 系统默认行为会退出程序。routeDelegate17 会报告退出程序的次数,由调用方决定如何处理

routerDelegate.exitCount.addListener(() {
      setState(() {
        exitCount = routerDelegate.exitCount.value;
        //实际应用中这里给出警告。 2 秒后 exitCount 恢复为 0
        if (exitCount == 1) {
          countText = '在首页按 back 键 $exitCount 次';
        }
        //实际应用中这里执行退出程序操作。 2 秒后 exitCount 恢复为 1
        if (exitCount == 2) {
          countText = '在首页按 back 键 $exitCount 次';
        }
        //2 秒内如果一直按会一直增加。
        else{
          countText = '在首页按 back 键 $exitCount 次';
        }
      });
 });
复制代码

exitCount 是 ExitCount 的实例. 和普通的计次不同,每次增加次数后, delay 时间后都会被减掉。 应用程序可以监听 value 的变化来决定是否要退出程序。

在使用方面就讲完了。下面开始闲聊。

RouterDelegate17 闲聊

随意转到任意页面

你可能会觉得,这是在用 Navigator2.0 吗?怎么感觉和 Navigator1.0 一样啊。就是要达成这样的效果。用已有的习惯和用法能解决问题,为什么要新造一套?除了增加使用成本,没什么好处。虽然 push,pop,replace 在外表上看是一样的,但实际能力还是有增强的。假设有依次 push 三个页面 A,B,C,想从 c 回来A,用 1.0 api要么再push 一个 A,要么 pop C,pop B ,都不是想要的解决办法。用 RouterDelegate17 可以直接 push(A),也就是说,Navigator2.0 可以直接方便的跳到任何页面(新页面或栈中的任何页面),没有任何副作用。

初始化路由栈的能力

在 Navigator1.0 中 用 [navigator.initialRoute] (api.flutter.dev/flutter/wid…) 只能设置一个初始路由。但是在有的情况下,需要初始化一个路由栈。比如一个应用有两个页面,首页和详情页面。通过deeplink 直接打开详情页面,这时就应该初始化路由栈 [首页,详情页面]。为什么要首页也要初始化?是为了和正常打开app时形成一样的路由栈。这一点很重要,否则无法统一处理跳转逻辑。RouterDelegate17.setInitialPages 可以方便的设置初始路由栈。

简化页面状态监听

本来呢,页面状态是可以用 Observer + RouteAware 的。但是呢,实现了 RouteAware 的方法还是不能直接用,因为最终还是得体现在页面中,可能还得 setState,或 用 ValueListenableBuilder 改变页面状态。RouterDelegate17 一步到位,直接给出 status ,status 本身就是一个 ValueListenable,可以直接用。使用也非常简单,直接一句代码就可以引用。(前文有讲)

方不方便还是主要原因,毕竟如果能用还是可以用的。但是如果用 RouteAware ,在获取当前路由的时候需要用 ModalRoute.of(context) 拿到当前路由。最终是通过 inheritWidget 的方式拿到路由。inheritWidget 有一个更新机制,当判断函数为真的时候,会进行刷新

bool updateShouldNotify(_ModalScopeStatus old) {
    return isCurrent != old.isCurrent ||
           canPop != old.canPop ||
           route != old.route;
}
复制代码

这个判断有时会导致不必要的刷新,而且 updateShouldNotify 是没法 override 的。 RouterDelegate17 没有这个烦恼,不用你自己去拿当前路由,自动管理了。

最后还有一个原因,RouteAware 对弹出 dialog 这种是不会监听的。没有监听就不能用和 page 一样的方式来处理页面状态改变。RouterDelegate17 把 dialog 和 page 统一处理。比如 在 PageA 中打开对话框,PageA 的状态变为 PageStatus.leave。在 PageA 中 打开新页面 PageB,PageA 的状态也变为 PageStatus.leave

为什么没有用状态驱动的方式

除了用这种 api 的方式,还有一种是状态状态驱动的方式。就是把 app 中与路由相关状态都拿出来做为状态来驱动路由的变更。这种感觉上很高级的样子,但实操上会丧失很多灵活性,并且让逻辑更加复杂。就拿 web 开发来说吧。无论是 React 还 是 Vue,最后还是落到 push 等方法上,而不是用状态驱动的方式。

其它

还有退出程序这种小福利就不讲了。知道方便就行了。

使用 Navigator 2.0 注意事项

我觉得目前 RouterDelegate17 是最舒服的使用 Navigator 2.0 的姿势了,可能你还觉得不够,如果你要自己实现或修改 RouterDelegate17 的话,需要注意一些问题。

  1. Navigator 的 key 要这样写
final _navigatorKey = GlobalKey<NavigatorState>();
  @override
  GlobalKey<NavigatorState>? get navigatorKey => _navigatorKey;
复制代码

而不是

@override
 GlobalKey<NavigatorState>? get navigatorKey => GlobalKey<NavigatorState>();
复制代码

我看到有人这样写了,所以解释一下。如果这样写,会导致 所有的 wiget 每次都重建,导致严重的性能问题。

完整示例代码点 [这里]

目录
相关文章
|
1月前
|
Dart Android开发 开发者
使用Flutter
使用Flutter
19 0
|
1月前
Flutter 之 Stepper
Flutter 之 Stepper Stepper 组件在移动端应用中经常被使用,它可以让用户通过一系列步骤来完成一个复杂的操作。Flutter 中的 Stepper 组件提供了一个简单的方式来实现这个功能。
|
1月前
Flutter中的OverflowBox、SizedOverflowBox,详细介绍
Flutter中的OverflowBox、SizedOverflowBox,详细介绍 在Flutter中,当一个widget的大小超出了其父widget的大小时,通常会发生溢出现象。为了解决这个问题,Flutter提供了两个widget:OverflowBox和SizedOverflowBox。
124 0
flutter系列之:使用SliverList和SliverGird
在上一篇文章我们讲解SliverAppBar的时候有提到过,Sliver的组件一般都用在CustomScrollView中。除了SliverAppBar之外,我们还可以为CustomScrollView添加List或者Grid来实现更加复杂的组合效果。 今天要向大家介绍的就是SliverList和SliverGird。
flutter系列之:使用SliverList和SliverGird
|
Dart 前端开发 JavaScript
Flutter快速了解
Flutter是Google开发的一套全新的跨平台、开源UI框架,支持iOS、Android系统开发,并且是未来新操作系统Fuchsia的默认开发套件。自从2017年5月发布第一个版本以来,目前Flutter已经发布了近60个版本,并且在2018年5月发布了第一个“Ready for Production Apps”的Beta 3版本,6月20日发布了第一个“Release Preview”版本。
使用 Flutter LinearGradient
使用 Flutter LinearGradient
390 0
使用 Flutter LinearGradient
|
缓存 开发工具 git
flutter技巧
flutter技巧