停止从 Flutter Listener 小部件冒泡的事件

Stop event bubbling from Flutter Listener widget

我正在 Flutter 中制作一个 Android 应用程序,我想添加一个交互式小部件来侦听原始指针事件并对其做出反应。使用 Listener 小部件很容易做到这一点:

class _MyWidget extends State<MyWidget> {
  @override
  build(BuildContext ctx) {
    return Listener(
      onPointerMove: (event) {
        event.delta // use this to do some magic...
      },
      child: ...
    );
  }
}

但是,此小部件在 PageView 内呈现,允许用户滑动以更改页面。这是我不想摆脱的行为 – 除了 用户滑动 MyWidget 的时间。由于 MyWidget 要求用户 touch-and-drag,我不希望他们不小心导航到不同的页面。

在浏览器中,这将非常容易实现,我只需调用 event.stopPropagation() 并且事件不会从 MyWidget 传播(即冒泡)到其祖先 PageView如何在 Flutter 中停止传播事件?

可以 ,理论上,让 MyWidget 设置应用程序状态,并在我滑动 [=] 时使用它来禁用 PageView 13=]。然而,这将违背 Flutter 小部件的自然数据流:它需要我在多个地方添加回调,并使所有小部件更加交织在一起,并且可重用性降低。我更愿意阻止事件冒泡,locally.


编辑:我试过使用AbsorbPointer,但它似乎在错误的方向“阻止传播”。它阻止 AbsorbPointer 的所有 children 接收指针事件。我想要的恰恰相反——阻止 child 上的指针事件传播到它的祖先。

在 Flutter 中,通常只有 1 个小部件会响应用户触摸事件,而不是触摸位置的所有部件都一起响应。为了确定哪个小部件应该响应触摸事件,Flutter 使用一种称为“手势竞技场”的东西来确定“赢家”。

Listener 是一个原始的监听小部件, 不会在“手势竞技场”中竞争,因此底层 PageView 仍将“获胜” “手势竞技场”,尽管 Listener 在技术上更接近用户(并且会赢)。

另一方面,GestureDetector 等更高级(较少原始)的小部件确实进入了“手势竞技场”竞争,并将“获胜”,从而导致 PageView“失败” "在竞技场中,所以页面浏览量不会移动。

例如,尝试将此代码放在 PageView 中并拖动到它上面:

GestureDetector(
  onHorizontalDragUpdate: (DragUpdateDetails details) {
    print('on Horizontal Drag Update');
  },
  onVerticalDragUpdate: (DragUpdateDetails details) {
    print('on Vertical Drag Update');
  },
  child: Container(
    width: 100,
    height: 100,
    color: Colors.red,
  ),
)

您应该注意到 PageView 不再滚动。

但是,根据 PageView 的滚动轴(无论是水平滚动还是垂直滚动),删除上面 GestureDetector 上的事件之一(例如删除 onHorizontalDragUpdate 事件侦听器在水平滚动 PageView) 将使 PageView 再次滚动。这是因为水平滑动和垂直滑动是进入不同手势领域的“non-conflicting事件”。

那么,回到您最初的问题,您能否使用这些“更高级”的小部件来重写您的业务逻辑,而不是处理原始级别的数据 Listener?如果是这样,问题将由框架为您解决。

如果您出于某种原因必须使用 Listener,我为您提供了另一种可能的解决方法:在 PageView 小部件中,您可以传入 physics: const NeverScrollableScrollPhysics() 以使其停止滚动。因此,您也许可以监视触摸事件,并相应地 set/clear 属性 。本质上,在“touch down”时,锁定 PageView,在“touch up”时,释放它。