Flutter PopScope对于iOS设置canPop为false无效问题

        这个问题应该出现很久了,之前的组件WillPopScope用的好好的,flutter做优化打算“软性”处理禁用返回手势,出了PopScope,这个组件也能处理在安卓设备上的左滑返回事件。但是iOS上面左滑返回手势禁用,一直无效。

当然之前一直在使用Navigator动态路由的方式的话是不影响的。

Navigator.push(context,MaterialPageRoute(
                        builder: (context) =>
                            FullScreenImagePage(images: [imagePath]),
                      ),
                    );

        但是对于静态路由和一些其他三方的路由(fluro,go_route,getx(这个在版本4.7.2中已修复))封装组件就会有影响。

那我们看下具体为什么会出现这种问题?

        首先我们看PopScope的内部实现是接口实现了PopEntry的一个ValueNotifier  canPopNotifier来控制内部的返回手势是否可用。

ModelRoute注册该事件,然后在这个抽象类中有统一的返回手势判断竞技场逻辑:

到此暂告一段落。

        上面我们说Navigator的动态路由方式没问题,那么就看下他的处理和其他的路由方式的差别:MaterialPageRoute的混入MaterialRouteTransitionMixin如下:

CupertinoPageRoute的混入CupertinoRouteTransitionMixin如下(我只截取了最有介绍的部分,想看的可以自己去文件里面看,路径我已经截出来了):

                

        Flutter最近也一直在优化Cupertinao的组件库,上面的CupertinoPageRoute就是iOS的路由处理,可以看到差异很明显,页面page(这篇文章主要介绍page形式)形式的代码有自定义实现了手势_CupertinoBackGestureDetector,他通过参数方法enabledCallback来动态获取左滑手势是否有效。它的内部实现如下:

        可以看到enabledCallback的来源是route.popGestureEnabled,到此就是最终的位置了。通过断点可以看到,(前提是二级页面设置了PopScope的canPop为false)当Navigator的动态路由方式的时候,此处是false,左滑返回无效,一般其他的方式路由来的此处是true,左滑返回可以。

        由此而来,问题就出现在了route的popGestureEnabled上面,我们可以聚焦在此处,点击寻根,可以追溯到ModalRoute的popGestureEnabled(上面已截图,本文第三张图),问题就出在了第三个判断中的popDisposition==RoutePopDisposition.doNotPop上面,再往上看popDisposition的来源如下:

        问题的原因应当是此处路由在iOS设备中路由的转换导致canPopNotifier的变更出现的问题。

下面我们参考看下Get的PageRoute解决关键代码:

        可以看到GetX的最新调整将路由的手势带出来,判断处理 解决此次Flutter的iOS PopScope问题。(当然这只是表象比较明显的位置,具体详细大家有兴趣可以自行点进去再细致研究)

Flutter 中,`PopScope` 是用于拦截导航返回操作的新组件,自 Flutter 3.16 起完全替代了之前的 `WillPopScope`。它提供了更清晰和灵活的 API,使开发者能够更好地控制页面是否允许返回操作。 ### PopScope 的基本使用方法 `PopScope` 的核心属性有两个: - `onPopInvoked`:一个回调函数,当用户尝试返回时触发。该函数接收一个布尔参数 `didPop`,表示系统是否已经执行了弹出操作。 - `canPop`:一个布尔值,用于决定是否允许页面被弹出。如果设置为 `false`,则用户无法通过返回按钮或手势返回上一个页面。 以下是一个基础的使用示例: ```dart PopScope( canPop: false, // 禁止直接返回 onPopInvoked: (bool didPop) { if (!didPop) { // 用户尝试返回但被阻止,可以在此处执行自定义逻辑,例如提示用户 print('返回操作被拦截'); } }, child: Scaffold( appBar: AppBar(title: Text('PopScope 示例')), body: Center(child: Text('尝试按下返回键')), ), ) ``` 在上面的示例中,用户尝试返回将被阻止,并在控制台输出一条信息。这可以用于实现退出确认对话框或保存未提交的数据等场景。 ### 复杂场景示例:结合导航栈使用 在嵌套导航或底部菜单导航中,可能会出现多个页面堆栈的情况。此时,`PopScope` 可用于控制特定页面的返回行为,而不会影响整个应用的导航流程。 以下示例展示如何在带有嵌套导航的页面中使用 `PopScope`: ```dart class NestedNavigationPage extends StatelessWidget { const NestedNavigationPage({super.key}); @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvoked: (bool didPop) { if (!didPop) { // 自定义逻辑,例如显示确认对话框 showDialog( context: context, builder: (context) => AlertDialog( title: Text('确定要离开吗?'), actions: [ TextButton(onPressed: Navigator.of(context).pop, child: Text('取消')), TextButton(onPressed: () => Navigator.of(context).pop(), child: Text('确认')), ], ), ); } }, child: Scaffold( appBar: AppBar(title: Text('嵌套导航页')), body: Center(child: Text('这是一个需要特殊返回处理的页面')), ), ); } } ``` ### PopScope 与 WillPopScope 的对比 | 特性 | WillPopScope | PopScope | |------------------|------------------------|-----------------------------------| | 是否被弃用 | 是 | 否 | | 是否支持同步判断 | 否 | 是 (`canPop`) | | 是否支持异步回调 | 是 (`onWillPop`) | 是 (`onPopInvoked`) | | API 清晰度 | 较低 | 更清晰、更直观 | ### 注意事项 - `canPop` 控制是否允许弹出,若设置为 `false`,用户尝试返回时不会自动关闭页面,但会触发 `onPopInvoked` 回调。 - `onPopInvoked` 中的 `didPop` 参数表示是否已经执行了弹出操作。如果为 `true`,说明页面已经被关闭;如果为 `false`,说明弹出被阻止。 - 在多层嵌套结构中,应确保 `PopScope` 被正确放置在需要拦截返回行为的页面或组件中 [^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值