-
Notifications
You must be signed in to change notification settings - Fork 29.7k
[flutter] reject mouse drags by default in scrollables #81569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[flutter] reject mouse drags by default in scrollables #81569
Conversation
| /// | ||
| /// This is used by scrollables to force mouse interactions to go through the | ||
| /// scroll wheel or scrollbar, instead of allowing drag gestures to move them. | ||
| final bool rejectMousePointers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be inverted to something like touchOnly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder should this be platform dependent? for example set it to true when platform is desktop or web. Do you know what is the android behavior it user uses mouse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really a platform adaption, its an adaption based on how the user is interacting with the device. My impression of android/ios is that mouse interaction is uncommon, and if it is apps are likely to do the wrong thing anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can use a set of acceptedPointerDevices, so it's not mouse-specific, and put it in ScrollBehavior so we can configure it by platform like @chunhtai suggests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea that when it is mouse, prevent drag scroll regardless of the platform?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally speaking scrolling with a mousewheel or scrollbar is more efficient that scrolling by a simulated touch gesture. By enabling mouse based drag in scrollables, flutter forces any gesture accepting widgets within those lists to compete with the scroll drag events. Disambiguation of these gestures isn't perfect and neither are the users movements - this leads to a harder to use application.
For example, try and drag to scroll to the right/left on the flutter dashboard. Because the left/right drags compete with the up/down drags its easy to get stuck accidentally. Of couse that also implies we need provide a way to keep this enabled, to support the nested scrollable with no scrollbar pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't think you should try to generalize this much. Its really unlikely that a gesture detector wants to disambiguate between, say, touch and inverse stylus. And if it does, it might be doing something entirely custom which would make extra configurability on our end a waste.
mouse and touch (stylus/inverse stylus) are really very separate kinds of interaction.
Piinks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome thank you!
I linked the issue this looks likely to resolve.
An earlier implementation of this broke DraggableScrollableSheet, but it looks like this isn't breaking anything 🎉 (still need to check g3, I've fired those tests off to see).
| _gestureRecognizers = <Type, GestureRecognizerFactory>{ | ||
| HorizontalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>( | ||
| () => HorizontalDragGestureRecognizer(), | ||
| () => HorizontalDragGestureRecognizer(rejectMousePointers: true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally we thought about putting this feature in ScrollBehavior. Down below here the _configuration is the ScrollBehavior setting up the velocityTrackerBuilder. That way folks can configure this as they like, or target subtrees by putting in another ScrollConfiguration.
| /// | ||
| /// This is used by scrollables to force mouse interactions to go through the | ||
| /// scroll wheel or scrollbar, instead of allowing drag gestures to move them. | ||
| final bool rejectMousePointers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can use a set of acceptedPointerDevices, so it's not mouse-specific, and put it in ScrollBehavior so we can configure it by platform like @chunhtai suggests.
| /// | ||
| /// This is used by scrollables to force mouse interactions to go through the | ||
| /// scroll wheel or scrollbar, instead of allowing drag gestures to move them. | ||
| final bool rejectMousePointers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea that when it is mouse, prevent drag scroll regardless of the platform?
| _gestureRecognizers = <Type, GestureRecognizerFactory>{ | ||
| VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>( | ||
| () => VerticalDragGestureRecognizer(), | ||
| () => VerticalDragGestureRecognizer(rejectMousePointers: true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the developer have a way to override this behavior? I wonder whether this should be a theme-thing. cc @HansMuller
edit: I don't know there was ScrollConfiguration, I think this can be in ScrollConfiguration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ScrollConfiguration seems like a reasonable choice - assuming it is only created for lists and things? Like draggable sheets need to still be draggable, but that doesn't use a scrollable correct?
|
Assuming we put this in the scroll configuration, I imagine the breaking change might go something like:
|
I vote for first, we should target the user experience |
|
Next patch is a little WIP, but moves the configuration onto the scroll behavior and then defaults it to the current incorrect behavior |
|
What is the over/under on adding a field to scroll behavior being a breaking change? i'm a bit concerned we're actually going to make this more breaking not less, to allow configuring a behavior that is IMO a bug. |
|
i guess as long as people are extending ScrollBehavior it should be OK |
Oh sorry I overlooked the original comment. I meant I vote for defaulting it to true to prevent mouse scrolling (or whatever you think the correct behavior is unless it break internal customer, then you probably need to do three phase transitions...), and I don't have opinion whether we expose and override or not. We can always expose it later if people want to change it. Not exposing it will however, probably get a lot of complain though |
|
@Hixie considerations for naming? I think the correct name would be something like |
|
How about rather than a boolean here we just have a set of pointer kinds that accept drags? Then you can name it just "devices" and "dragDevices" or some such. (That would also let people decide if styluses should allow drags. I suspect there are UIs where you'd want a stylus to draw and a finger to drag.) |
I don't think it will be that breaking, we just made a bunch of changes to ScrollBehavior and I already migrated the |
Awesome! |
Piinks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
| () => VerticalDragGestureRecognizer(supportedDevices: _configuration.dragDevices), | ||
| (VerticalDragGestureRecognizer instance) { | ||
| instance | ||
| ..onDown = _handleDragDown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: These could probably be chained to match the rest readability-wise. :)
| () => VerticalDragGestureRecognizer(supportedDevices: _configuration.dragDevices), | |
| (VerticalDragGestureRecognizer instance) { | |
| instance | |
| ..onDown = _handleDragDown | |
| () => VerticalDragGestureRecognizer(), | |
| (VerticalDragGestureRecognizer instance) { | |
| instance | |
| ..onDown = _handleDragDown | |
| ..supportedDevices = _configuration.dragDevices |
| /// The device types that this gesture recognizer will accept drags from. | ||
| /// | ||
| /// If not specified, defaults to all pointer kinds. | ||
| final List<PointerDeviceKind> supportedDevices; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a set instead?
| || oldDelegate.overscrollIndicator != overscrollIndicator | ||
| || oldDelegate.physics != physics | ||
| || oldDelegate.platform != platform | ||
| || listEquals(oldDelegate.dragDevices, dragDevices) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably use set otherwise the order difference may cause a rebuild.
also we should set the type? i.e. listEquals<PointerDeviceKind>
|
I wasn't sure whether to prefer a set or list, I mostly chose a list because I could use |
|
Triggered a global test run manually, so it likely won't update here. Everything passed. 🎉 Will set the G check green manually. |
|
Updated to use a set, and to set the supported devices in the gesture factory callback |
| } | ||
|
|
||
| const Set<PointerDeviceKind> _kAllPointerDeviceKinds = <PointerDeviceKind>{ | ||
| ...PointerDeviceKind.values, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know we could do this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤩
|
I'm still having this problem. horizontal scroll within a vertical scroll. However, fluent-ui package has a HorizontalScollView that works. However, they don't show a scrollbar. |
|
@talksik can you please file a new issue with:
Thanks! |
Currently flutter web and desktop apps allow scrolling by dragging with the mouse. This feels unnatural compared to other desktop applications, and makes it harder to use more complex mouse gestures on items that are contained within a scrollable and impossible to accurately select text (since the text selection drag competes with the scroll drag).
This is likely a breaking change, so the old scrollable behavior is configurable via scroll behavior:enableMouseDragNothing broke!
Fixes #71322