如何防止在父小部件GestureDetector上触发onTapDown?

时间:2018-11-09 18:57:48

标签: flutter gesture-recognition

我有一个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,也会打印“红色”和“绿色”。

2 个答案:

答案 0 :(得分:1)

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

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

因此,红色 GestureDetector绿色 GestureDetector的子控件。 绿色 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,
      ),
    )
  ],
)

答案 1 :(得分:0)

如Flutter开发者之一所述:https://github.com/flutter/flutter/issues/18450#issuecomment-397372078

opaque仅表示手势检测器的整个大小都是命中区域,并不阻止孩子被命中。要阻止对孩子的点击,请使用IgnorePointerAbsorbPointer

您必须添加某种条件,以用IgnorePointer包装孩子的Widget,这样顶部的GestureDetector将吸收水龙头。

在您的示例中,在这些小部件之间添加IgnorePointer

GestureDetector(
  onTapDown: (_) {
    print("Green");
  },
  child: IgnorePointer(
    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,
          ),
        ),
      ),
    ),
  ),
);