Flutter:在iOS和Android中禁用滑动返回功能

42

我刚开始学习Flutter开发,在iOS上使用导航抽屉时,当您滑动打开它时,会执行Navigation.of(context).pop()。我想在iOS中禁用这种“滑动返回”行为。我一直在查阅相关文档,但没有什么收获。

我看到有人提到了一个WillPopScope,似乎可以解决问题(GitHub的问题页面在此处),但我不确定这是否是“正确”的方法(它似乎太复杂了……应该更简单些,比如在根应用程序中设置一个选项)。

8个回答

71

WillPopScope正确的方法。

(看起来有些复杂... 应该更简单... 就像根应用程序上的一个设置一样)。

它并不复杂。只需要一行代码:

WillPopScope(
  onWillPop: () async => false,
  child: <children here>
)

使用配置文件会使事情变得更加复杂,因为它更难阅读和维护。

并且请记住在Flutter中所有东西都是小部件,而不仅仅是其中的一半。认证、配置,一切皆是如此。


1
嘿@Darky,我们成为朋友了吗:P我找到了一个不错的方法来做这件事...我即将发布我的答案。 - James Gilchrist
有没有办法在保持默认的向右滑动返回iOS行为(即return true;)的同时利用WillPopScope呢? - Jonathan Rhein
这在命名路由上也适用吗?使用WillPopScope无法禁用它... - th_lo
在InAppWebView作为子项实现时无法工作。我们如何在InAppWebView中防止这种情况发生?@RémiRousselet - amit.flutter
在Android上,将其设置为false仍然会显示全局滑动控件,并在我尝试在小部件中做出反应之前捕获它。例如,我有一个视频刮擦条,它是页面的宽度。如果我从0开始并尝试刮到视频的中间,应用程序(或手机?)认为我正在尝试滑动并仍然显示默认的“从边缘滑动”控件并捕获手势,而我的刮擦器无法对其做出反应。如果我将其设置为false,则不会导航回来,但它仍会捕获滑动,同时仅停留在页面上(而不是刮擦)。 - claudekennilol
显示剩余6条评论

39
我这里有一个额外的观点。我刚刚解决了这个问题,但我还需要让用户能够通过按下AppBar上的“原生”返回按钮返回(不想因为这个问题重新实现AppBar),然后我发现了这个小众的标志:userGestureInProgress在Navigator对象上,所以我使用的是(并且假设这是首选的方式):
onWillPop: () async {
    return !Navigator.of(context).userGestureInProgress;
},

1
无法在Android上工作。userGestureInProgress始终返回false... - poqueque
4
您可以这样简化它: onWillPop: () async => !Navigator.of(context).userGestureInProgress - TheFabbius
@poqueque 我遇到了同样的问题,只有在安卓上返回false。我已经为Flutter提出了一个问题,欢迎你给它点赞 https://github.com/flutter/flutter/issues/130401 - Guy Luz

30

MaterialPageRoute有一个名为fullscreenDialog的参数,默认情况下设置为false。当设置为true时,您的页面将以稍微不同的动画显示,并且在iOS上向右滑动返回功能将被禁用。

示例用法:


 Navigator.of(context).push(
        MaterialPageRoute(builder: (_) => HomePage(), fullscreenDialog: true));

请查看这里的讨论:https://github.com/flutter/flutter/issues/14203


2
这对我来说非常完美,适用于条款和条件页面。谢谢。 - Nero

10
您可以在Widget构建中尝试此操作:
@override
  Widget build(BuildContext context) {
    return WillPopScope(//forbidden swipe in iOS(my ThemeData(platform: TargetPlatform.iOS,)
      onWillPop: ()async {
        if (Navigator.of(context).userGestureInProgress)
          return false;
        else
          return true;
      },
      child: <your child>,
    );
  }

onWillPop可以像这样缩短:onWillPop: () async => !(Navigator.of(context).userGestureInProgress), - wattson

6

我曾经遇到一个类似的问题,想要在导航中阻止向后滑动以返回(默认的弹出函数)。这段代码帮助解决了问题。

@override
Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => false,
      child: Scaffold(
        body: Center(
          child: Text('Example'),
        ),
      ),
    );
  }

这段代码现在可以阻止iOS和Android的后退导航(默认后退按钮),这正是我想要的。希望对你有所帮助。


3

好的,如@Darky所说,WillPopScope是一个完全可接受的答案,然而,如果您想要在整个应用中禁用它,您可以实际上执行以下操作。

在xcode中打开您的项目,找到AppDelegate.swift并添加以下内容:

let controller: FlutterViewController
    = window?.rootViewController as! FlutterViewController;
controller.navigationController?
    .interactivePopGestureRecognizer?.isEnabled = false;

4
哎呀!说实话我觉得这很糟糕。你不应该触碰 iOS/Android 文件夹,只能用 Dart 编码。如果你真的想在所有地方禁用它,那么应该使用 Dart 编写并传递给 MaterialAppWidgetApp - Rémi Rousselet
哦,我同意,应该有一个设置或属性可以简单地传递到你的 MaterialApp 的根部,但是遗憾的是没有。:'( - James Gilchrist
修改Flutter源代码,甚至无需离开你的IDE。你可以在应用程序中设置断点、编辑和热重载Flutter源代码。 - Rémi Rousselet
1
你的修复方案基本上是将所有内容包装在WillPopScope中。应该很容易。 - Rémi Rousselet

3
无论是按下返回按钮还是进行滑动手势(iOS),都会在onWillPop处得到一个回调函数。该回调函数返回一个Future对象。如果Future对象返回true,则屏幕会被弹出(即导航到前一个屏幕);如果它返回false,则不会向后导航。
Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(title: Text("Second Screen"),),
        body: Center(

如果您想根据条件弹出屏幕,则声明

bool shouldPop = true; // change this using setState() inside build on the requirement. 

  onWillPop: () {
        return Future.value(shouldPop? true: false);
      },

3
我不太确定你想要实现什么,但大多数情况下人们想要摆脱返回功能是因为他们不想让用户控制认证机制。例如,用户登录后,您不希望他们通过按“返回”按钮(或在iOS上向后滑动)导航到登录页面。一个潜在的解决方案是使用pushNamedAndRemoveUntilFuture<T> pushNamedAndRemoveUntil<T extends Object>(BuildContext context, String newRouteName, RoutePredicate predicate) 引用:
将具有给定名称的路由推入最紧密地包围给定上下文的导航器中,然后删除所有先前的路由,直到predicate返回true。
示例代码: pushNamedAndRemoveUntil(context, '/home', ModalRoute.withName('/home')); 注意:请谨慎使用此方法,因为您可能会弄乱导航历史记录。

那并不能完全解决所有情况...即使使用左侧面板/抽屉滑动手势打开,它有时也会导航回去。 - James Gilchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接