-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
TL;DR
We propose that pointer enter, exit, and hover callbacks are split from Listener into a new class Mouse. It is mainly because mouse tracking is unable to support certain features provided by Listener, also because Listener is often used only for mouse events.
Problem
Unable to support HitTestBehavior.deferToChild
Although Listener is designed to be a all-in-one class for pointer-related callbacks, under the hood it consists of two subsystems: Mouse tracking, represented by onPointer{Enter,Exit,Hover} events, are implemented with layer-based hit testing, while pointer events listening, represented by onPointer{Down,Up,Move}, are implemented with render-box-based hit testing.
Listener also provides a few options, one of them being HitTestBehavior behavior, which controls the behavior of hit testing. However, this option is directly passed to RenderProxyBoxWithHitTestBehavior, therefore it can not affect layers (hence the layer-based callbacks). Having an option that only affects some of the callbacks is definitely confusing and undesired.
We can try to simulate the behavior in layers (which is planned), but it is technically impossible to simulate HitTestBehavior.deferToChild, since child information is related with render objects and is lost when converted to layers.
By splitting onPointer{Enter,Exit,Hover} into a separate class, we can provide a different type of behavior that has only two options to avoid this confusion.
It is often used alone
Listener.onPointer{Down,Up,Move} are rarely used directly, instead developers are supposed to use GestureDetector when possible, or to create a custom gesture recognizer with RawGestureDetector.
On the other hand, Listener.onPointer{Enter,Exit,Hover} are currently the only way to track mouse movement. This leads to a common code pattern as the following snippet, where Listener is only used for onPointer{Enter,Exit,Hover}:
@override
Widget build(BuildContext context) {
return Listener(
onPointerEnter: _handlePointerEnter,
onPointerExit: _handlePointerExit,
child: GestureDetector(
onTapDown: _handleTapDown,
onTap: _handleTap,
...
child: widget.child,
),
);
}Although we squashed 2 separate subsystems into Listener, developers often utilize only one of them. Therefore it might be a better idea to split them for modularity.
More mouse features are coming
Flutter plans to add more features related to mouse (for example, pointer icons). For features that are built based on the MouseTracker, the new APIs will be added to the same place, i.e. Listener. By splitting the mouse tracking part out, we keep Listener clean for apps that don't plan to support mouse.
Proposal
Split the mouse tracking part of RenderPointerListener into a new class RenderMouseListener.
Split the mouse tracking part of Listener into a new class Mouse. Listener will keep onPointer{Down,Up,Move,Cancel,Signal} as well as behavior.
Rename the mouse tracking callbacks of Mouse and MouseTrackerAnnotation to onMouse{Enter,Exit,Hover} to emphasize the difference.
- I understand that this change is more aggressive and less necessary. Comments are welcome.
Listener.onPointer{Enter,Exit,Hover} are preserved for backward compatibility and marked as deprecated.
A proposal PR is #36217.
Future plans
New behavior option for mouse tracking will be added in a separate PR after this change.
Revisions
Previously I proposed to split pointer events (up/down/move/cancel/signal) out while keeping enter/exit/hover in. The current proposal suggest the opposite.
Previously I proposed to call the new class MouseListener. The current proposal is calling Mouse to accommodate upcoming changes such as pointer icon.