【问题标题】:How do I prevent onTapDown from being triggered on a parent widgets GestureDetector?如何防止在父小部件 GestureDetector 上触发 onTapDown?
【发布时间】:2019-04-13 09:09:27
【问题描述】:

我有一个堆栈,可以在其中拖动几个小部件。此外,Stack 所在的容器有一个 GestureDetector 可以在 onTapDown 和 onTapUp 上触发。我希望这些 onTap 事件仅在用户点击堆栈中的小部件外部时触发。我试过以下代码:

class Gestures extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _GesturesState();
}

class _GesturesState extends State<Gestures> {
  Color background;
  Offset pos;

  @override
  void initState() {
    super.initState();
    pos = Offset(10.0, 10.0);
  }

  @override
  Widget build(BuildContext context) => GestureDetector(
    onTapDown: (_) => setState(() => background = Colors.green),
    onTapUp: (_) => setState(() => background = Colors.grey),
    onTapCancel: () => setState(() => background = Colors.grey),
        child: Container(
          color: background,
          child: Stack(
            children: <Widget>[
              Positioned(
                top: pos.dy,
                left: pos.dx,
                child: GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onPanUpdate: _onPanUpdate,
//                  onTapDown: (_) {},  Doesn't affect the problem
                  child: Container(
                    width: 30.0,
                    height: 30.0,
                    color: Colors.red,
                  ),
                ),
              )
            ],
          ),
        ),
      );

  void _onPanUpdate(DragUpdateDetails details) {
    RenderBox renderBox = context.findRenderObject();
    setState(() {
      pos = renderBox.globalToLocal(details.globalPosition);
    });
  }
}

但是,当开始拖动小部件时,最外层容器的 onTap 也会被触发,在这种情况下背景会暂时变绿。设置HitTestBehavior.opaque 似乎不像我期望的那样工作。也不会将 onTapDown 的处理程序添加到堆栈中的小部件。

那么,当用户与 Stack 内的小部件交互时,如何防止 onTapDown 在最外面的 GestureDetector 上被触发?

更新:

我遇到的问题的一个更简单的例子:

GestureDetector(
  onTapDown: (_) {
    print("Green");
  },
  child: Container(
    color: Colors.green,
    width: 300.0,
    height: 300.0,
    child: Center(
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTapDown: (_) {
          print("Red");
        },
        child: Container(
          color: Colors.red,
          width: 50.0,
          height: 50.0,
        ),
      ),
    ),
  ),
);

当我点击并按住红色容器时,即使内部 GestureDetector 有 HitTestBehavior.opaque,“Red”和“Green”都会打印出来。

【问题讨论】:

  • 不知道能不能帮到你,但是你试试chanhe外GestureDetectorListener
  • @AndreyTurkovsky 我不确定我是否理解你的意思。只需将外部检测器替换为侦听器并使用 onPointerDown 等,就像我使用 onTapDown 一样可以解决问题。 (这是一个简化的示例,在实际代码中,外部 GestureDetector 在不同的 Widget 中位于堆栈的更上方。因此,如果可能,我不希望更改该 GestureDetector)
  • 我遇到了同样的问题,你找到解决办法了吗?
  • @RasitAyaz 我为此创建了pub.dev/packages/transparent_pointer
  • 我试过那个包,但它似乎不适用于我的情况。父母的onTap 函数没有被触发,但它的onTapDown 函数正在被触发。有什么建议吗?

标签: flutter gesture-recognition


【解决方案1】:

在这个答案中,我将解决您给出的更简单的示例。您正在创建以下 Widget 层次结构:

 - GestureDetector       // green
   - Container
     - Center
       - GestureDetector // red
          - Container

因此 red GestureDetectorgreen GestureDetector 的子 Widget。 green GestureDetector 具有默认的HitTestBehaviorHitTestBehavior.deferToChild。这就是为什么onTapDown 被两个容器触发的原因。

遵从其子代的目标在其子代内接收事件 仅当他们的孩子之一被命中测试命中时才限制。

相反,您可以使用 Stack 来构建您的 UI:

 - Stack
   - GestureDetector // green
     - Container
   - GestureDetector // red
     - Container

这种结构会产生以下源代码。它看起来一样,但行为是您想要的:

Stack(
  alignment: Alignment.center,
  children: <Widget>[
    GestureDetector(
      onTapDown: (_) {
        print("Green");
      },
      child: Container(
        color: Colors.green,
        width: 300.0,
        height: 300.0,
      ),
    ),
    GestureDetector(
      onTapDown: (_) {
        print("Red");
      },
      child: Container(
        color: Colors.red,
        width: 50.0,
        height: 50.0,
      ),
    )
  ],
)

【讨论】:

    【解决方案2】:

    我发现我可以通过创建一个具有如下命中测试行为的渲染对象来获得我想要的行为(使小部件对它的父级透明,同时仍然响应指针事件):

      @override
      bool hitTest(BoxHitTestResult result, {@required Offset position}) {
        // forward hits to our child:
        final hit = super.hitTest(result, position: position);
        // but report to our parent that we are not hit when `transparent` is true:
        return false;
      }
    

    我在此处发布了一个带有具有此行为的小部件的包:https://pub.dev/packages/transparent_pointer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-17
      • 1970-01-01
      • 2020-03-09
      • 2016-09-30
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多