颤动悬停式触摸处理 - 无需抬起手指即可检测来自不同小部件的触摸

Let*_*tbe 7 mobile hover touch-event flutter

假设我Container在屏幕上有 3秒通过改变颜色来对触摸做出反应。当用户的手指在它们上面时,它们应该改变颜色,当触摸末端时,它们应该恢复正常。我想要的是这些容器在用户手指/指针指向它们时做出反应,即使触摸是在容器外部的屏幕上的随机区域开始的。所以基本上我正在寻找的东西就像 css 悬停。

如果我GestureDetector单独包装每个容器,它们将不会对在它们之外开始的触摸事件做出反应。在另一个问题上(不幸的是,我现在没有链接)建议将所有容器用一个包装起来,GestureDetector并GlobalKey为每个容器分配一个以区分它们。

这是检测触摸手势的板:

class MyBoard extends StatefulWidget {
  static final keys = List<GlobalKey>.generate(3, (ndx) => GlobalKey());

  ...
}

class _MyBoardState extends State<MyBoard> {
  ...

  /// I control the number of buttons by the number of keys,
  /// since all the buttons should have a key
  List<Widget> makeButtons() {
    return MyBoard.keys.map((key) {
      // preapre some funcitonality here
      var someFunctionality;
      return MyButton(key, someFunctionality);
    }).toList();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (tapDownDetails) {
        handleTouch(context, tapDownDetails.globalPosition);
      },
      onTapUp: (tapUpDetails) {
        finishTouch(context);
      },
      onPanUpdate: (dragUpdateDetails) {
        handleTouch(context, dragUpdateDetails.globalPosition);
      },
      onPanEnd: (panEndDetails) {
        finishTouch(context);
      },
      onPanCancel: () {
        finishTouch(context);
      },
      child: Container(
        color: Colors.green,
        width: 300.0,
        height: 600.0,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: makeButtons(),
        ),
      ),
    );
  }

Run Code Online (Sandbox Code Playgroud)

这是简单的MyButton:

class MyButton extends StatelessWidget {
  final GlobalKey key;
  final Function someFunctionality;
  MyButton(this.key, this.someFunctionality) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<KeyState>(
      builder: (buildContext, keyState, child) {
        return Container(
          width: 100.0,
          height: 40.0,
          color: keyState.touchedKey == this.key ? Colors.red : Colors.white,
        );
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在_MyBoardState这就是我如何处理其检测MyButton触摸:

MyButton getTouchingButton(Offset globalPosition) {
    MyButton currentButton;
    // result is outside of [isTouchingMyButtonKey] for performance
    final result = BoxHitTestResult();

    bool isTouchingButtonKey(GlobalKey key) {
      RenderBox renderBox = key.currentContext.findRenderObject();
      Offset offset = renderBox.globalToLocal(globalPosition);
      return renderBox.hitTest(result, position: offset);
    }

    var key = MyBoard.keys.firstWhere(isTouchingButtonKey, orElse: () => null);
    if (key != null) currentButton = key.currentWidget;

    return currentButton;
  }
Run Code Online (Sandbox Code Playgroud)

我正在使用提供程序包,而不是重建整体MyBoard,setState只重建相关的相关部分MyButton。仍然每个按钮都有一个监听器,它在每次更新时重建,我正在使用GlobalKey. 在颤振文档上,它说:

全局密钥相对昂贵。如果您不需要上面列出的任何功能,请考虑改用 Key、ValueKey、ObjectKey 或 UniqueKey。

但是,如果我们查看getTouchingButton方法,我需要GlobalKey获取renderBoxobject 并获取currentWidget. 我还必须为每个GlobalKey. 这个计算在onPanUpdate触发时重复,这意味着当用户的手指移动一个像素时。

UI 更新速度不够快。通过单击(以常规速度点击和点击),您通常看不到MyButtons上的任何变化。

如果我需要总结我的问题,当手指以更有效和优雅的方式悬停在不同小部件上时,如何检测来自不同小部件的相同单点触摸(无提升)?

Let*_*tbe 6

由于还没有人回答,我正在分享我自己最近想出的解决方案。现在我每次触摸时都能看到视觉反应。它足够快,但仍然感觉 UI 上有一点延迟,而且感觉有点 hacky 而不是一个具体的解决方案。如果有人可以提供更好的解决方案,我可以将其标记为已接受的答案。


我的解决方案:

要事第一;因为我们必须检测平移手势并且我们正在使用onPanUpdate和onPanEnd,所以我可以擦除onTapDown和onTapUp,而不是只使用onPanStart。这还可以检测非移动的简单点击触摸。

我也不再使用Providerand Consumer,因为它会Consumer在每次更新时重建所有 s 。当s数量MyButton增多时这确实是一个大问题。我没有保持MyButton简单和虚拟,而是将触摸处理工作移入其中。每个都保存着当前是否被触摸MyButton的数据。

更新按钮是这样的:

class NewButton extends StatefulWidget {
  NewButton(Key key) : super(key: key);
  @override
  NewButtonState createState() => NewButtonState();
}

class NewButtonState extends State<NewButton> {
  bool isCurrentlyTouching = false;

  void handleTouch(bool isTouching) {
    setState(() {
      isCurrentlyTouching = isTouching;
    });
    // some other functionality on touch
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100.0,
      height: 40.0,
      color: isTouched ? Colors.red : Colors.white,
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这NewButtonState不是私有的。globalKey.currentState.handleTouch(bool)我们将在检测到触摸手势的地方使用。