So I have a widget GestureDetector
in Flutter and I want it to do something on "hover", like this (dragged in any direction):
Is it possible to do it with GestureDetector
? Any help appreciated.
MouseRegion is used when it is needed to compare the list of objects that a mouse pointer is hovering over between this frame and the last frame. This means entering events, exiting events, and mouse cursors. To listen to general pointer events, use Listener, or more preferably, GestureDetector.
The main difference between InkWell and GestureDetector lies in Material widget. InkWell must have a Material Widget as an ancestor. However, there is no such compulsion for GestureDetector widget. Otherwise, in many ways, these two widgets share common features.
The MouseRegion
The MouseRegion Widget MouseRegion exposes a few events related to, you guessed it, your mouse. 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. 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.
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"),
),
),
);
}
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.
If it's collide -> return hovered If it's not -> not hovered
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 e.g. DartPad will not be responsive or otherwise behave oddly in your device/simulators (i.e. MouseRegion.onHover
will fire on a tap for mobile, which may be confusing if you didn't read the property closely).
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. (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.)
Unfortunately you can't use GestureDetecture
for that, instead you have to use 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.
Instead I will use 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"),
),
),
);
}
I'm not familiar with a method for doing this with GestureDetector
since I believe all gestures need to start inside the GestureDetector
widget. It may be possible to wrap a container inside a GestureDetector
and determine when the pointer is over top of the container.
If you're trying to do this on a Web or desktop device, you can use MouseRegion widget. If you go to the link, you can see how easily it detects mouse entries and exits.
There's also the Draggable widget that can be used in conjunction with the DraggableTarget
widget to do some cool things.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With