在Flutter中如何检测手势检测器是否被悬停?

22

我在Flutter中有一个小部件GestureDetector,我想让它在“悬停”时执行某些操作,就像这样(在任何方向拖动):

example

是否可以使用GestureDetector来实现?感谢任何帮助。

8个回答

24

MouseRegion

MouseRegion小部件 MouseRegion会暴露一些与鼠标有关的事件。这些事件包括:

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.

所有这些实际上都是指针事件,它们返回有关用户指针的各种信息。在这里,我们将查看用户鼠标在包含小部件内的当前位置,该位置存储在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"),
                ), 
            ),
        );

    }

1
MouseRegion的事件是否也会被手势触发?(例如在iOS和Android中,而不是在Web或桌面上) - Hossein Hadi

6
对于任何想在移动设备上进行此操作的人,请注意,触摸界面实际上不处理“悬停”概念,而不像桌面浏览器。因此,在移动设备/模拟器中,提出MouseRegion.onHover|Enter|ExitInkwell.onHoverListener.onPointerHover等解决方案,它们看起来在DartPad中调试时还可以,但是在您的设备/模拟器上将不会响应或以其他方式表现奇怪(例如,MouseRegion.onHover 将在移动端触发点击事件,如果您没有仔细阅读属性,则可能会感到困惑)。
移动端的解决方案涉及使用Listener.onPointerMove并手动对选择区域进行RenderBox的命中测试,已经在这里这里讨论过。 (后者还有一个明确计算自己使用Rect表示边界框的答案,该边界框位于您要击中的全局坐标系中。据推测Path.contains可以用于执行非矩形命中区域的相同操作。)

2

很遗憾,您不能使用 GestureDetecture,而是需要使用 MouseRegion

我正在使用 答案 的思路,但我会进行修改,因为它对我不起作用。 该答案使用了 PointerDetails,但对我没有用。 相反,我将使用 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"),
                ), 
            ),
        );

    }

就我今天发现的而言,您还可以使用具有内置悬停功能的 InkWell Widget ! - Chrissi

2
Inkwell小部件可以实现这一点。您只需要将您的小部件包装在它周围即可。您可以用Inkwell替换Gesture Detector。示例代码如下:
InkWell(
      onHover: (value)
      {
        //Do something
      },
        onTap: ()
      {
        //Do something
      },
      child: Container(),

)


2

您可以创建一个非常小的框,当拖动时将其移动,然后实现一些碰撞(使用此包:https://pub.dev/packages/slowly_moving_widgets_field),并检查您的小框是否与GestureDetector的框发生碰撞。

如果发生碰撞 -> 返回hovered 如果没有 -> 不是hovered


1

我不熟悉使用 GestureDetector 实现此功能的方法,因为我认为所有手势都需要在 GestureDetector 小部件内开始。可能可以将容器包装在 GestureDetector 中,并确定指针何时位于容器顶部。

如果您正在尝试在Web或桌面设备上执行此操作,则可以使用 MouseRegion 小部件。如果您转到链接,您可以看到它如何轻松检测鼠标进入和退出。

还有 Draggable 小部件,可与 DraggableTarget 小部件一起使用,以执行一些很酷的事情。


谢谢!我会尝试的。当用户在屏幕上滑动手指时,它能在移动设备上工作吗? - Yanb

0
  1. 使用GestureDetector包装父部件。这是为了满足在目标小部件范围之外触发悬停事件的情况。
  2. 获取应响应“悬停”事件的小部件的位置/区域。确保在构建小部件树之前即Stateful小部件的initState中执行此操作。此外,这应该是相对于视口的绝对位置。您不希望出现屏幕滚动时满足以下条件的情况
  3. 向GestureDetector添加侦听器,并使用回调函数监听和更新当在目标小部件范围内检测到“悬停”时。

简而言之,如果目标边界位于坐标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;
 }

是的。但是当父部件中有许多子部件时,检查哪个部件被悬停可能不是很有效率。 - Hossein Hadi

0

您可以将您的Gesturedetector或任何小部件包装在Listener类中,并监听拖动的位置,根据这些值执行一些任务。


1
如果触摸是从我的小部件外部开始的,则没有事件会在“Listener”小部件上触发。例如,在Listener文档中,如果您从蓝色小部件外部开始手势操作,则当悬停在蓝色小部件上时不会发生任何事情。 - Hossein Hadi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接