irpas技术客

Flutter: iOS 侧滑返回_J_D_Chi_flutter 侧滑返回

网络投稿 2995

文章目录 写在前面内容将 onWillPop 置为 null重写 MaterialPageRoute 其它

写在前面

在 Flutter 里,默认是支持 iOS 的屏幕边缘侧滑返回的,但如果由于一些需求,我们对 WillPopScope的onWillPop回调进行了重写,就会导致这个特性失效。

内容

一般情况下,我们没有对页面添加 WillPopScope这个 Widget 并重写它的 onWillPop方法,在 iOS 上是可以在屏幕左侧边缘进行侧滑返回的。但如果我们重写了 onWillPop方法,就会发现这个手势特性失效了。在router.dart文件里,我们可以知道为什么:

// route.dart static bool _isPopGestureEnabled<T>(PageRoute<T> route) { ... // If attempts to dismiss this route might be vetoed such as in a page // with forms, then do not allow the user to dismiss the route with a swipe. if (route.hasScopedWillPopCallback) return false; .... // Looks like a back gesture would be welcome! return true; } // routes.dart abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T> { ... void addScopedWillPopCallback(WillPopCallback callback) { assert(_scopeKey.currentState != null, 'Tried to add a willPop callback to a route that is not currently in the tree.'); _willPopCallbacks.add(callback); } /// True if one or more [WillPopCallback] callbacks exist. /// /// This method is used to disable the horizontal swipe pop gesture supported /// by [MaterialPageRoute] for [TargetPlatform.iOS] and /// [TargetPlatform.macOS]. If a pop might be vetoed, then the back gesture is /// disabled. /// /// The [buildTransitions] method will not be called again if this changes, /// since it can change during the build as descendants of the route add or /// remove callbacks. /// /// See also: /// /// * [addScopedWillPopCallback], which adds a callback. /// * [removeScopedWillPopCallback], which removes a callback. /// * [willHandlePopInternally], which reports on another reason why /// a pop might be vetoed. @protected bool get hasScopedWillPopCallback { return _willPopCallbacks.isNotEmpty; } ... } // will_pop_scope.dart class _WillPopScopeState extends State<WillPopScope> { ModalRoute<dynamic>? _route; @override void didChangeDependencies() { super.didChangeDependencies(); if (widget.onWillPop != null) _route?.removeScopedWillPopCallback(widget.onWillPop!); _route = ModalRoute.of(context); if (widget.onWillPop != null) _route?.addScopedWillPopCallback(widget.onWillPop!); } @override void didUpdateWidget(WillPopScope oldWidget) { super.didUpdateWidget(oldWidget); assert(_route == ModalRoute.of(context)); if (widget.onWillPop != oldWidget.onWillPop && _route != null) { if (oldWidget.onWillPop != null) _route!.removeScopedWillPopCallback(oldWidget.onWillPop!); if (widget.onWillPop != null) _route!.addScopedWillPopCallback(widget.onWillPop!); } } }

由于我们重写了 onWillPop,它会把这个回调加入到路由里的_willPopCallbacks列表里,因此这里就禁用了退出的手势操作。

将 onWillPop 置为 null

假如我们确实需要在一些情况下使用到 WillPopScope,那么应该怎么处理呢?我们可以看到把回调加入到队列的前提是 widget.onWillPop != null,所以我们可以结合具体情况,提供一个触发条件,来改变这个onWillPop,类似下面:

bool condition = true; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: condition ? () async{ // do something return true; } : null, child: Scaffold( ); }

这样我们就可以在较为灵活地处理这个问题了。

在 Document that WillPopScope prevents swipe to go back on MaterialPageRoute #14203 这个问题里,我们可以看到另外的处理方法。

重写 MaterialPageRoute class CustomMaterialPageRoute extends MaterialPageRoute { @override @protected bool get hasScopedWillPopCallback { return false; } CustomMaterialPageRoute({ required WidgetBuilder builder, RouteSettings? settings, bool maintainState = true, bool fullscreenDialog = false, }) : super( builder: builder, settings: settings, maintainState: maintainState, fullscreenDialog: fullscreenDialog, ); }

通过重写 MaterialPageRoute,直接将 hasScopedWillPopCallback 改为 false,并在跳转的时候修改实现:

// old: Navigator.push( context, MaterialPageRoute(builder: (context) { return const Second(); }), ); // new: Navigator.push( context, CustomMaterialPageRoute(builder: (context) { return const Second(); }), );

但此举会导致的一个问题是,如果你想在返回页面的时候传递数据回去,即使你重写了 onWillPop回调,通过手势返回是不会返回数据的,但 AppBar 上的返回按钮,还是可以将数据返回的。

其它

在处理底部弹窗的返回处理的时候,发现了一个 Android 与 iOS 上的交互不同之处。在 Android 上只要手指在屏幕左侧边缘右滑,就可以收起弹窗。而 iOS 需要你的手指有一个向下的方向,才能收起弹窗。

在 iOS 上,我们总可以感觉到它的手势操作是非常跟手的,如果这个东西是从哪来的,它就应该原路回去。具体可以看[WWDC 2018] Designing Fluid Interfaces 流畅的界面设计。

所以我想这可能是 WillPopScope 在 Android 和 iOS 上的表现有所差异的原因。在 Android 上,手指侧滑,就等同于底部返回按钮,很明确的表示要回退。而在 iOS 上,因为它非常的跟手,你虽然在屏幕边缘侧滑,但只要你手指还在屏幕上,你是可以再放回去的。这样在 WillPopScope 的判断意图上,就可能不像 Android 那么清晰。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Flutter #侧滑返回 # #里默认是支持 #iOS