likes
comments
collection
share

Flutter 之将 WillPopScope 转化为 PopScope

作者站长头像
站长
· 阅读数 8

往期优选推荐

推荐指数: ⭐️⭐️⭐️⭐️⭐️

》fam 和 flutter_gen 相比优点《

  1. fam 不需要在项目的 pubspec.yaml 文件的 dev_dependencies 引入任何东西;而 flutter_gen 需要引入 flutter_gen_runner

  2. fam 在导入资源后只需要执行 fam run 即可;而 flutter_gen 需要执行 dart run build_runner build 。需要注意的是 dart run build_runner build 是很多代码自动生成的运行指令(多语言、json解析、rividerpod...等)。所以一旦执行就需要很长时间才能完成,而 fam 拥有独立的体系,便捷快速!

  3. fam 的资源管理类在项目中使用简短;而 flutter_gen 需要使用.语法一直到资源文件,然后再通过 .path 获取资源路径,这很麻烦!

  4. fam 拥有独立的系统体系; flutter_gen 和多语言的使用有一定的冲突。

推荐指数: ⭐️⭐️⭐️⭐️⭐️

该篇文章包含了 dev_prokit 所有功能介绍和示例的目录。此文章能够让开发者快速的了解 dev_prokit 都提供了那些功能,以及能更快速的找到自己需要的功能介绍和应用。

本期功能介绍

一、起因

在近期公司项目的优化升级过程中,当我们将Flutter版本从3.13.6升级到3.19.6时,我们不幸地发现了一个显著的变动:WillPopScope这一组件被标记为已废弃。具体的提示信息如下所示:

Flutter 之将 WillPopScope 转化为 PopScope

将鼠标放在 WillPopscope 上就会出现提示如下:

Flutter 之将 WillPopScope 转化为 PopScope

上面的提示的意思是 : 在 v3.12.0-1.0.pre 版本之后 WillPopScope.new 已弃用,不应使用,请改用 PopScope 。那么,关于 PopScope 的奥秘,它究竟蕴含了哪些参数与独特的方法?我们又该如何巧妙地运用它呢?接下来,就让我们一起踏上这场探索之旅吧。

二、 PopScope 详解

  • PopScope 源码

    const PopScope({
      super.key,
      required this.child,
      this.canPop = true,
      this.onPopInvoked,
    });
    
    final Widget child;
    final PopInvokedCallback? onPopInvoked;
    final bool canPop;
    

    从上图源码看 PopScope 有四个属性相比 WillPopScope 多了一个属性。下面我们介绍 PopScope 属性。

  • PopScope 属性介绍

    • Key? key

      PopScope 组件的唯一标识。

    • Widget child

      树中此小部件(PopScope)下方的小部件(child)。

    • PopInvokedCallback? onPopInvoked

      该属性方法是在处理路由弹出后调用。

      📢 注意: 调用此方法不能阻止路由弹出,因为该方法是在路由弹出后调用的。

    • bool canPop

      该属性是设置是否阻止当前路由弹出。为 false 时阻止路由弹出。

      📢 注意:如果路由的子树中出现多个 PopScope 组件,则要每个 canPop 都为 true 时才可以返回。

三、 PopScope 应用思路

PopScope 的应用起始也很简单,大致步骤如下:

  • 首先 : 确认该页面是否返回拦截, 需要拦截就将 PopScopecanPop 设置为 false,否则 PopScope 组件就无需添加;
  • 其次 : 实现 PopScopeonPopInvoked 方法,在该方法里面编写阻止弹出的逻辑;
  • 然后 : 在阻止弹出逻辑中,要判断满足什么条件需要阻止路由弹出以及什么条件下不需要阻止路由弹出, 在满足不需要阻止路由弹出的条件后调用 Navigator.of(context).pop() 或者你自定义的路由返回方法即可。

从上面的大致步骤看, PopScope 实现路由返回拦截是不是很简单!

四、PopScope 应用示例

  • 场景 1

    通过按钮跳转到一个页面,该页面有一个输入框。在导航返回时,如果输入框为空,则直接退出;如果输入框不为空,则需要弹出二次确认框询问是否退出该页面。如果用户选择 取消,则不退出当前页面;如果选择 确定,则退出当前页面。

  • 代码如下

    final TextEditingController textEditingController = TextEditingController();
    PopScope(
     canPop: false,
     onPopInvoked: (didPop) async {
       if (textEditingController.text.isNotEmpty) {
         final result = await showDialog(
           context: context,
           builder: (context) {
             return Center(
               child: Row(
                 children: [
                   IconButton(
                     onPressed: () {
                       appRouter.pop(false);
                     },
                     icon: Text('取消'),
                   ),
                   IconButton(
                     onPressed: () {
                       appRouter.pop(true);
                     },
                     icon: Text('确定'),
                   )
                 ],
               ),
             );
           },
         );
         if (result ?? false) {
           appRouter.pop();
         }
       }
     }
    

    上面代码实现了阻止导航返回功能。但是其中有个不友好的体验,那就是当 canPopfalse 时,IosAndroid 的侧滑返回就不能使用了。如果想使用侧滑功能那么我们就需要动态的更改 canPop 的值。

  • PopScope 需要保留 IosAndroid 侧滑功能 (扩展)

    要使用 PopScope 保留侧滑功能需要将 canPop 参数设置为 true。 示例如下:

    StreamController streamController = StreamController();
    StreamBuilder(
      initialData: true,
      stream: streamController.stream,
      builder: (context, snapshot) {
        return PopScope(
          canPop: snapshot.data ?? true,
          onPopInvoked: (didPop) async {
            if (didPop == false) {
              final result = await showDialog(
                context: context,
                builder: (context) {
                  return Center(
                    child: Row(
                      children: [
                        IconButton(
                          onPressed: () {
                            appRouter.pop(false);
                          },
                          icon: Text('取消'),
                        ),
                        IconButton(
                          onPressed: () {
                            appRouter.pop(true);
                          },
                          icon: Text('确定'),
                        )
                      ],
                    ),
                  );
                },
              );
              if (result ?? false) {
                appRouter.pop();
              }
            }
          },
          child: TextField(
            onChanged: (value) => streamController.add(value.isEmpty),
          ),
        );
      },
    }
    

    这样就可以使用侧滑功能以及二次弹框逻辑。这种模式会导致根部树进行 build 也有可能导致页面数据出现重置情况。这种情况可以使用 Key? key 进行优化。示例代码如下:

    PopScope(
      canPop: true,
      onPopInvoked: (didPop) {//... 省略}
      child: TextField(
        key: ValueKey('Only one!'),
        onChanged: (value) => streamController.add(value.isEmpty),
      ),
    )
    

五、 PopScope 目前存在问题

PopScope 目前在 导航 1.0 使用表现还是可以的,但是在 导航 2.0 以及一些第三方插件 go_router 应用中出现一些 onPopInvoked 方法可以回调又是不回调的情况,相关问题可以关注这个话题,链接如下: PopScope seems to be incompatible with GoRouter

六、鼓励与支持

这便是 dev_prokit 提供的方法之一。以编写代码较少、功能完善、代码运行强壮、支持面广等形式来开发 dev_prokit 包。 如果你有 Idea 或者 Opinion ,那请你点击此处 》》Idea & Opinion《《 把它留下吧!如果你在使用该功能后感觉还可以,它能够给你的项目开发带来便捷,那请你为之点个⭐️ dev_prokit ⭐️ 吧!分享给更多需要它的开发者。

转载自:https://juejin.cn/post/7380702373570166810
评论
请登录