简体   繁体   English

如何检测 GestureDetector 是否悬停在 Flutter 中?

[英]How do I detect if a GestureDetector is hovered in Flutter?

So I have a widget GestureDetector in Flutter and I want it to do something on "hover", like this (dragged in any direction):所以我在 Flutter 中有一个小部件GestureDetector ,我希望它在“悬停”时做一些事情,就像这样(向任何方向拖动):

例子

Is it possible to do it with GestureDetector ?可以用GestureDetector吗? Any help appreciated.任何帮助表示赞赏。

The MouseRegion鼠标区

The MouseRegion Widget MouseRegion exposes a few events related to, you guessed it, your mouse. MouseRegion 小部件 MouseRegion 公开了一些与鼠标相关的事件,您猜对了。 These events are:这些事件是:

OnEnter, which will trigger when your mouse enters the Widget.
OnExit, which will trigger when your mouse leaves the Widget.
OnHover, which will trigger both when your mouse enters the Widget and on every subsequent move inside the Widget.

All of these actually are PointerEvents which return all sorts of informations regarding the user's pointer.所有这些实际上都是 PointerEvents,它返回有关用户指针的各种信息。 Here, we're going to be looking at the current position of the user's mouse inside the containing Widget which is stored under the guise of an Offset.在这里,我们将查看用户鼠标在包含 Widget 中的当前 position,该 Widget 以 Offset 的名义存储。

 class _MyHoverableWidgetState extends State<MyHoverableWidget>{
        // Our state
        bool amIHovering = false;
        Offset exitFrom = Offset(0,0);
    
        return MouseRegion(
            onEnter: (PointerDetails details) => setState(() => amIHovering = true),
            onExit: (PointerDetails details) => setState(() { 
                amIHovering = false;
                exitFrom = details.localPosition; // You can use details.position if you are interested in the global position of your pointer.
            }),
            child: Container(
                width: 200,
                height: 200,
                child: Center(
                    child:Text(amIHovering ? "Look mom, I'm hovering" : "I exited from: $exitFrom"),
                ), 
            ),
        );

    }

For anyone looking to do this on mobile , note that a touch interface doesn't actually handle the concept of "hover" unlike your desktop browser, so solutions proposing MouseRegion.onHover|Enter|Exit , Inkwell.onHover , or Listener.onPointerHover that look ok fiddling in eg DartPad will not be responsive or otherwise behave oddly in your device/simulators (ie MouseRegion.onHover will fire on a tap for mobile, which may be confusing if you didn't read the property closely).对于希望在移动设备上执行此操作的任何人,请注意触摸界面实际上并不处理“悬停”的概念,这与您的桌面浏览器不同,因此建议MouseRegion.onHover|Enter|ExitInkwell.onHoverListener.onPointerHover的解决方案看起来不错,例如 DartPad 中的摆弄将不会响应或在您的设备/模拟器中表现奇怪(即MouseRegion.onHover将在点击移动设备时触发,如果您没有仔细阅读该属性,这可能会造成混淆)。

Solutions for mobile that involve using a Listener.onPointerMove and manually hitTesting the RenderBox for the selection area has been discussed here and again here .涉及使用Listener.onPointerMove和手动点击测试选择区域的RenderBox的移动解决方案已在此处此处再次讨论。 (The latter also has an answer that explicitly calculates it yourself using Rect s representing the bounding boxes in the global coordinate frame for whatever you want to hit. Presumably Path.contains could be used to do the same for non-rectangular hit zones.) (后者也有一个答案,使用Rect s 自己明确计算它,代表全局坐标系中的边界框,无论你想击中什么。大概Path.contains可以用来对非矩形击中区域做同样的事情。)

You can create a really small box and move it along when dragging, then implement some collision (this package: https://pub.dev/packages/slowly_moving_widgets_field ), and check whether your small box is collide with GestureDetector's box or not.您可以创建一个非常小的盒子并在拖动时将其移动,然后实现一些碰撞(此 package: https://pub.dev/packages/slowly_moving_widgets_field ),并检查您的小盒子是否与 GestureDetector 碰撞。

If it's collide -> return hovered If it's not -> not hovered如果碰撞 -> 返回悬停 如果不是 -> 未悬停

I'm not familiar with a method for doing this with GestureDetector since I believe all gestures need to start inside the GestureDetector widget.我不熟悉使用 GestureDetector 执行此操作的方法,因为我相信所有手势都需要在 GestureDetector 小部件内开始。 It may be possible to wrap a container inside a GestureDetector and determine when the pointer is over top of the container.可以将容器包装在 GestureDetector 中并确定指针何时位于容器顶部。

If you're trying to do this on a Web or desktop device, you can use MouseRegion widget.如果您尝试在 Web 或桌面设备上执行此操作,则可以使用MouseRegion小部件。 If you go to the link, you can see how easily it detects mouse entries and exits.如果您将 go 链接到该链接,则可以看到它检测鼠标进入和退出的难易程度。

There's also the Draggable widget that can be used in conjunction with the DraggableTarget widget to do some cool things.还有可以与DraggableTarget小部件结合使用的 Draggable 小部件来做一些很酷的事情。

Unfortunately you can't use GestureDetecture for that, instead you have to use MouseRegion .不幸的是,您不能为此使用GestureDetecture ,而是必须使用MouseRegion

I'm using the idea of that answer but I will correct it, because it doesn't work (for me).我正在使用该答案的想法,但我会纠正它,因为它不起作用(对我来说)。 The answer used PointerDetails which does not work for me.答案使用了对我不起作用PointerDetails Instead I will use PointerEvent .相反,我将使用PointerEvent

// inside your current widget
class _MyHoverableWidgetState extends State<MyHoverableWidget>{

        // some previous code of your class //


        // define a class variable to store the current state of your mouse pointer
        bool amIHovering = false;

        // store the position where your mouse pointer left the widget
        Offset exitFrom = Offset(0,0);
    
        return MouseRegion(
            // callback when your mouse pointer enters the underlying widget
            // here you have to use 'PointerEvent'
            onEnter: (PointerEvent details) => setState(() => amIHovering = true),
            
            // callback when your mouse pointer leaves the underlying widget
            onExit: (PointerEvent details) => setState(() { 
                amIHovering = false;
                // storing the exit position
                exitFrom = details.localPosition; // You can use details.position if you are interested in the global position of your pointer.
            }),

            // your underlying widget, can be anything
            child: Container(
                width: 200,
                height: 200,
                child: Center(
                    child:Text(amIHovering ? "Look mom, I'm hovering" : "I exited from: $exitFrom"),
                ), 
            ),
        );

    }

You can wrap your Gesturedetector or any widget with Listener class and listen to the position of the drag and based on those values you can perform some tasks.您可以使用侦听器 class 包装您的 Gesturedetector 或任何小部件,并侦听拖动的 position 并根据这些值执行一些任务。

  1. Wrap the parent widget with a Gesture detector.用手势检测器包裹父小部件。 This is to satisfy the case where the hover event is triggered outside the bounds of the target widget.这是为了满足在目标小部件范围之外触发 hover 事件的情况。
  2. Get the position/area of the widget that should respond to the "hover" event.获取应该响应“悬停”事件的小部件的位置/区域。 Ensure that you do this before the widget tree is built ie initState for Stateful widgets.确保在构建小部件树之前执行此操作,即有状态小部件的 initState。 Also, this should be absolute to the viewport.此外,这应该是绝对的视口。 You dont want a case where when the screen is scrolled the condition(below) is satisfied您不希望屏幕滚动时满足条件(下)的情况
  3. Add a listener to the gesture detector with a callback function that listens and updates when the "hover" is detected within the bounds of the target widget.使用回调 function 向手势检测器添加一个监听器,当在目标小部件的范围内检测到“悬停”时进行监听和更新。

So, in a nutshell if the target bounds lie within the coordinates x1(0), x2(10), y1(0), y2(8)因此,简而言之,如果目标边界位于坐标 x1(0)、x2(10)、y1(0)、y2(8) 内

// Sample callbackCode to check if within
// x and y are the positions returned from the gesture detector
bool isWithinTarget(x, y)
{
 if (x > x1 && x < x2 && y > y1 && y < y2) {
   // The pointer is within the target widget
   // Perform required action
   // Rebuild widget tree if necessary
   return true;
 }
 return false;
 }

The Inkwell widget can do this. Inkwell 小部件可以做到这一点。 You only need to wrap your widget around it.您只需要将您的小部件包裹在它周围。 You can replace Gesture Detector with the Inkwell.您可以用 Inkwell 替换 Gesture Detector。 Sample code looks like:示例代码如下所示:

InkWell(
      onHover: (value)
      {
        //Do something
      },
        onTap: ()
      {
        //Do something
      },
      child: Container(),

)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM