在 Flutter 中,定位的 Widget 如何感觉在其父 Stack 区域之外的点击?

Mar*_*rcG 18 gesture flutter flutter-layout

AStack包含MyWidget在 a 内部Positioned

Stack(
  overflow: Overflow.visible,
  children: [
    Positioned(
    top: 0.0,
    left: 0.0,
    child: MyWidget(),
  )],
);
Run Code Online (Sandbox Code Playgroud)

由于溢出Overflow.visibleMyWidget比大Stack,它显示之外的Stack,这就是我想要的。

但是,我无法在区域MyWidget外的Stack区域中点击。它只是忽略了那里的水龙头。

我如何确保MyWidget在那里接受手势?

Nor*_*ert 11

出现此行为是因为堆栈在检查子项是否被命中之前先检查指针是否在其边界内:

类:RenderBox(由 RenderStack 扩展)

bool hitTest(BoxHitTestResult result, { @required Offset position }) {

    ...

    if (_size.contains(position)) {
      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
        result.add(BoxHitTestEntry(this, position));
        return true;
      }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

我的解决方法是删除

if (_size.contains(position))
Run Code Online (Sandbox Code Playgroud)

查看。不幸的是,如果不从框架中复制代码,这是不可能的。

这是我所做的:

  • 复制Stack类并命名为Stack2
  • 复制 RenderStack 并将其命名为 RenderStack2
  • 使 Stack2 参考 RenderStack2
  • 在没有 _size.contains 检查的情况下从上面添加了 hitTest 方法
  • 复制 Positioned 并将其命名为 Positioned2 并使其引用 Stack2 作为其通用参数
  • 在我的代码中使用了 Stack2 和 Positioned2

这种解决方案绝不是最优的,但它实现了所需的行为。

  • 是否已在 Github 上向 Flutter 团队提出更改框架的建议?似乎可以向 Stack 添加一个属性,以允许指针事件超出 Stack 的边界 (7认同)

小智 7

我有一个类似的问题。基本上,由于堆栈的子项不使用完全溢出框大小进行命中测试,因此我使用了嵌套堆栈和任意大高度,以便我可以捕获嵌套堆栈溢出框的点击次数。不确定它是否适合你,但这里什么也没有:)

所以在你的例子中,也许你可以尝试这样的事情

Stack(
  clipBehavior: Clip.none,
  children: [
    Positioned(
    top: 0.0,
    left: 0.0,
    height : 500.0 // biggest possible child size or just very big 
    child: Stack(
      children: [MyWidget()]
    ),
  )],
);
Run Code Online (Sandbox Code Playgroud)


小智 7

可以考虑使用继承来复制hitTest打破命中规则的方法,例如

class Stack2 extends Stack {
  Stack2({
    Key key,
    AlignmentGeometry alignment = AlignmentDirectional.topStart,
    TextDirection textDirection,
    StackFit fit = StackFit.loose,
    Overflow overflow = Overflow.clip,
    List<Widget> children = const <Widget>[],
  }) : super(
          key: key,
          alignment: alignment,
          textDirection: textDirection,
          fit: fit,
          overflow: overflow,
          children: children,
        );

  @override
  RenderStack createRenderObject(BuildContext context) {
    return RenderStack2(
      alignment: alignment,
      textDirection: textDirection ?? Directionality.of(context),
      fit: fit,
      overflow: overflow,
    );
  }
}

class RenderStack2 extends RenderStack {
  RenderStack2({
    List<RenderBox> children,
    AlignmentGeometry alignment = AlignmentDirectional.topStart,
    TextDirection textDirection,
    StackFit fit = StackFit.loose,
    Overflow overflow = Overflow.clip,
  }) : super(
          children: children,
          alignment: alignment,
          textDirection: textDirection,
          fit: fit,
          overflow: overflow,
        );

  @override
  bool hitTest(BoxHitTestResult result, {Offset position}) {
    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
      result.add(BoxHitTestEntry(this, position));
      return true;
    }
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)


die*_*per 6

好的,我对此做了一个解决方法,基本上我GestureDetector在父级上添加了一个并实现了onTapDown. 此外,您还必须跟踪您的Widget使用情况GlobalKey以获得当前位置。

Tap检测到父级别时,检查点击位置是否在您的小部件内部。

代码如下:

final GlobalKey key = new GlobalKey();

      void onTapDown(BuildContext context, TapDownDetails details) {
        final RenderBox box = context.findRenderObject();
        final Offset localOffset = box.globalToLocal(details.globalPosition);
        final RenderBox containerBox = key.currentContext.findRenderObject();
        final Offset containerOffset = containerBox.localToGlobal(localOffset);
        final onTap = containerBox.paintBounds.contains(containerOffset);
        if (onTap){
          print("DO YOUR STUFF...");
        }
      }

      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTapDown: (TapDownDetails details) => onTapDown(context, details),
          child: Container(
            color: Colors.red,
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            child: Align(
              alignment: Alignment.topLeft,
                      child: SizedBox(
                width: 200.0,
                height: 400.0,
                child: Container(
                  color: Colors.black,
                    child: Stack(
                      overflow: Overflow.visible,
                      children: [
                        Positioned(
                          top: 0.0, left: 0.0,
                                          child: Container(
                            key: key,
                            width: 500.0,
                            height: 200.0,
                            color: Colors.blue,
                          ),
                        ),
                      ],
                    ),

                ),
              ),
            ),
          ),
        );
      } 
Run Code Online (Sandbox Code Playgroud)

  • 那么,您实际上是在检测“MyWidget”是否被点击?假设它是长方形的。嗯,我想这对于某些用例很有用,所以我会投票赞成这个答案。但就我而言,“MyWidget”是一个复杂的小部件,它需要接收自己的手势才能完成工作。例如,如果它包含一个ListView和按钮,这个解决方案仍然不能解决问题。 (2认同)

无夜之*_*之星辰 5

有时你可以用来Column构建一个特殊的Stack

结果

关键点:

  1. 垂直方向向上。
  2. 向下变换顶部小部件。

以下是我的代码,你可以复制并测试:

Column(
  verticalDirection: VerticalDirection.up,
  children: [
    Container(
      width: 200,
      height: 100,
      color: Colors.red,
    ),
    Transform.translate(
      offset: const Offset(0, 30),
      child: GestureDetector(
        onTap: () {
          print('tap orange view');
        },
        child: Container(
          width: 60,
          height: 60,
          color: Colors.orange,
        ),
      ),
    ),
  ],
),
Run Code Online (Sandbox Code Playgroud)