flutter - 在 Stack 中的两个小部件之间传递所有手势

标签 flutter

我正在开发一个应用程序,我在 map 上显示标记,如下所示:
enter image description here
它的工作方式是标记在“ map ”小部件上呈现为 Stack .我的问题是,目前,标记“吸收”了用于控制下方 map 的手势(如果手势在标记上开始)。
因此,我想知道,有没有办法在堆栈中的两个小部件之间传递所有手势事件?理想情况下,标记将忽略(并通过)除 onTap 之外的所有事件。 (因为我仍然希望能够点击标记)。
这是我的特定树:
enter image description here
干杯!

最佳答案

当(视觉上)位于同一堆栈中另一个小部件顶部的小部件被命中时,堆栈将停止任何进一步的 HitTest 。因此,在您的情况下,包含 GoogleMap 的堆栈的第二个子项必须让widget报告没有命中,所以栈会给GoogleMap有机会对指针事件使用react。 IgnorePointer可以做到这一点,但是该小部件也不会对其子项进行 HitTest ,因此其子手势检测器永远不会涉及任何手势。在简单的情况下,可以通过交换 IgnorePointer 的顺序来解决这个问题。和 GestureDetector同时设置后者的 behavior属性(property)到HitTestBehaviour.translucent .例如:

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Stack(
          fit: StackFit.expand,
          children: [
            GestureDetector(
              onDoubleTap: () => print("double red"),
              child: Container(color: Colors.red),
            ),
            Positioned(
              top: 100,
              left: 100,
              right: 100,
              bottom: 100,
              child: GestureDetector(
                behavior: HitTestBehavior.translucent,
                onTap: () => print("green"),
                child: IgnorePointer(
                  child: Container(color: Colors.green),
                ),
              ),
            ),
          ],
        ),
      );
}
不过你的情况比较复杂。更通用的方法是创建一个新的小部件,如 IgnorePointer (让我们称它为 TransparentPointer )可以对它的父级采取行动,就好像它永远不会被命中一样,同时仍然对其子级进行 HitTest 。这里我复制了 IgnorePointer并以这种方式改变了行为(关于 RenderIgnorePointer 的唯一变化是在 RenderTransparentPointer.hitTest 中):
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Stack(
          fit: StackFit.expand,
          children: [
            GestureDetector(
              onDoubleTap: () => print("double red"),
              child: Container(color: Colors.red),
            ),
            TransparentPointer(
              transparent: true,
              child: Stack(
                children: [
                  Positioned(
                    top: 100,
                    left: 100,
                    right: 100,
                    bottom: 100,
                    child: GestureDetector(
                      onTap: () => print("green"),
                      child: Container(color: Colors.green),
                    ),
                  )
                ],
              ),
            ),
          ],
        ),
      );
}

class TransparentPointer extends SingleChildRenderObjectWidget {
  const TransparentPointer({
    Key key,
    this.transparent = true,
    Widget child,
  })  : assert(transparent != null),
        super(key: key, child: child);

  final bool transparent;

  @override
  RenderTransparentPointer createRenderObject(BuildContext context) {
    return RenderTransparentPointer(
      transparent: transparent,
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderTransparentPointer renderObject) {
    renderObject
      ..transparent = transparent;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<bool>('transparent', transparent));
  }
}

class RenderTransparentPointer extends RenderProxyBox {
  RenderTransparentPointer({
    RenderBox child,
    bool transparent = true,
  })  : _transparent = transparent,
        super(child) {
    assert(_transparent != null);
  }

  bool get transparent => _transparent;
  bool _transparent;

  set transparent(bool value) {
    assert(value != null);
    if (value == _transparent) return;
    _transparent = value;
  }

  @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 !transparent && hit;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<bool>('transparent', transparent));
  }
}

我将此代码发布为一个小包:https://pub.dev/packages/transparent_pointer

关于flutter - 在 Stack 中的两个小部件之间传递所有手势,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65269190/

相关文章:

flutter - 安装build_runner时Dart SDK版本不匹配

http - 'GoogleHttpClient.send'('Future<StreamedResponse> Function(BaseRequest)')不是 'IOClient.send'的有效替代

flutter - 什么时候需要将异步函数的返回类型声明为 future 对象?

json - Flutter - 保存用户数据

flutter - 是否可以在 dart 中创建字符串扩展 `isEmptyOrNull` ?

flutter - dart Grpc 的拦截器

android - 如何在输入值时动态更改文本字段的宽度

flutter - 当用户在 Flutter 中按下键盘上的按键时调用函数

android - Flutter从GetStorage列表中获取数据值

list - Dart 映射,如何根据其键 id 替换 list<map> 中的特定键值?