IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter: iOS 侧滑返回 -> 正文阅读

[移动开发]Flutter: iOS 侧滑返回

写在前面

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

内容

一般情况下,我们没有对页面添加 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 那么清晰。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-14 21:17:24  更:2022-02-14 21:17:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:36:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码