-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Tapping outside of SelectableRegion should dismiss the selection
#176843
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
Tapping outside of SelectableRegion should dismiss the selection
#176843
Conversation
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.
Code Review
This pull request introduces a welcome enhancement to SelectableRegion, allowing selections to be dismissed by tapping outside the region. The implementation correctly uses TapRegion for this purpose. The accompanying test case effectively validates the new behavior. My review includes a minor suggestion for code consistency in text_selection.dart.
9b1f032 to
d8aeefa
Compare
| ); | ||
| } | ||
| return TextFieldTapRegion(child: ExcludeSemantics(child: handle)); | ||
| return TapRegion( |
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 extracted into SelectableRegionTapRegion, but not sure if we wanted to do that in this PR. Also instead of wrapping two TapRegions here we could just use one and have the groupId be provided.
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.
Can this share the same group id as textfield so we don't create 2?
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.
talked offline, this will cause textfield and selection area to share the same tap region and not unfocus one when the other receives tap
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.
Talked offline: I think if we used the same groupId as TextField, wouldn't we run into a situation where if we have a focused TextField and a SelectionArea in a column and we press the SelectionArea, the TextField will not lose focus because it is the same TapRegion as SelectionArea.
86df217 to
64ee725
Compare
This reverts commit 64ee725.
cb0bff8 to
db5486c
Compare
|
This PR adds the basic default functionality. I plan to do a follow up PR making this overridable similar to |
justinmc
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 👍. I'm glad you ended up finding this clean solution, I was worried it would be a lot more complicated!
Is this one of the things that SelectableRegion needs for parity with SelectableText by the way?
| return TapRegion( | ||
| groupId: SelectableRegion, | ||
| onTapOutside: (PointerDownEvent event) { | ||
| // To match the native web behavior, this selectable region is |
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 this behavior the same on mobile and desktop?
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.
From my testing they behave the same. I also tried native frameworks like jetpack compose and macOS swift ui and this seems like it also matches the behavior. I tried iOS swift UI but it technically doesn't have a widget that let's you granularly select static text, it's Text().textSelection(.enabled) only works for macOS, and on iOS it selects everything (context menu pops up with "copy") and doesn't show selection handles or a highlight. I tried using a UITextView but sometimes it dismissed the selection and sometimes it did not, i'm not sure if this is due to the interaction between SwiftUI and UIViews so I haven't changed the existing behavior for native iOS.
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 checked WinUI 3 on Windows, it also does not dismiss the text selection if you click outside of it (unless your click moves the focus).
In my initial review, I also wondered if this was correct. It might be worth updating the comment to clarify that this is indeed intentional. Maybe something like:
Tapping outside of the text selection does not dismiss it on Android, macOS, and Windows.
| child: result, | ||
| return TapRegion( | ||
| groupId: SelectableRegion, | ||
| onTapOutside: (PointerDownEvent event) { |
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 correct behavior that the text should be unselected when the user taps anywhere, including on the selection or on unselected text? I guess those parts are already handled elsewhere in the code?
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.
Kind of, the expectation is when tapping inside the selection area the gesture recognizers handle this. So for example for a tap inside a selection area the selection would be set to the tapped position.
| await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.handlePlatformMessage( | ||
| 'flutter/lifecycle', | ||
| message, | ||
| (_) {}, |
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: Not important, but maybe replace the _ with the type and variable name if you get a chance.
| // The selection only dismisses when unfocused if the app | ||
| // was currently active. |
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.
Good catch.
| // Tap just outside the top-left corner of the selectable region | ||
| // to dismiss the selection. | ||
| final Rect selectableRegionRect = tester.getRect(find.byType(SelectableRegion)); | ||
| await tester.tapAt(selectableRegionRect.topLeft - const Offset(10.0, 10.0)); | ||
| await tester.pump(); | ||
| expect(paragraph.selections, isEmpty); |
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: Reading this test, I'm also curious what would happen if you tapped inside the selectableRegionRect. I think it also dismissed? Is that tested anywhere?
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.
Taps inside the selectable region rect will be handled by the selectable regions gesture recognizers, a basic tap inside the selectable region will move the selection to the precise/nearest position. This should already be covered by the gesture tests in selectable_region_test.dart.
chunhtai
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
| ); | ||
| } | ||
| return TextFieldTapRegion(child: ExcludeSemantics(child: handle)); | ||
| return TapRegion( |
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.
talked offline, this will cause textfield and selection area to share the same tap region and not unfocus one when the other receives tap
loic-sharma
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!
Roll Flutter from a873a27309b0 to 891d7d539256 (32 revisions) flutter/flutter@a873a27...891d7d5 2025-10-18 [email protected] Roll Skia from b4981b621a54 to 74df18176924 (1 revision) (flutter/flutter#177204) 2025-10-18 [email protected] Roll Fuchsia Linux SDK from ZHuhfPyyV-LcKDLRh... to M8WT2GMY46e_0fFho... (flutter/flutter#177200) 2025-10-18 [email protected] [material/dropdown_menu.dart] Refactor _RenderDropdownMenuBody.computeDryLayout (flutter/flutter#176503) 2025-10-18 [email protected] Roll Skia from 579c72d673dd to b4981b621a54 (2 revisions) (flutter/flutter#177199) 2025-10-17 [email protected] Roll Skia from 89a8bc508a7c to 579c72d673dd (15 revisions) (flutter/flutter#177194) 2025-10-17 [email protected] [test_fixes] Enable `deprecated_member_use_from_same_package`. (flutter/flutter#177183) 2025-10-17 [email protected] [a11y] fix table semantics cache for cells (flutter/flutter#177073) 2025-10-17 [email protected] [web] Self-cleaning service worker (flutter/flutter#176834) 2025-10-17 [email protected] Manual roll Skia from 2d9df7c70b6f to 89a8bc508a7c (24 revisions) (flutter/flutter#177182) 2025-10-17 [email protected] Fixing WindowManagerTest::DialogCanNeverBeFullscreen possibly hanging (flutter/flutter#177179) 2025-10-17 [email protected] [VPAT][A11y][a11y-app] Add a text label to slider. (flutter/flutter#177130) 2025-10-17 [email protected] Manual roll Dart SDK from a4485e5ef821 to a66f334fee2a (5 revisions) (flutter/flutter#177142) 2025-10-17 [email protected] [web][a11y] Fix the semantics tree reconstruction logic when a subtree is reparented to another node. (flutter/flutter#177069) 2025-10-17 [email protected] `SelectableRegion` should use flutter rendered menu on the web for Android and iOS (flutter/flutter#177122) 2025-10-17 [email protected] Refactor: Convert Title widget to StatefulWidget (flutter/flutter#176010) 2025-10-17 [email protected] Bump AGP, KGP, Gradle Templates (flutter/flutter#176858) 2025-10-17 [email protected] Roll Packages from 835dccb to 3747006 (3 revisions) (flutter/flutter#177170) 2025-10-17 [email protected] Mark windows_unopt test as flakey (flutter/flutter#177173) 2025-10-17 [email protected] Tapping outside of `SelectableRegion` should dismiss the selection (flutter/flutter#176843) 2025-10-17 [email protected] Roll Fuchsia Linux SDK from _dd0Jv50H0oUI2Ad8... to ZHuhfPyyV-LcKDLRh... (flutter/flutter#177137) 2025-10-17 [email protected] Manual roll Dart to a4485e5ef821 (3.11.0-25.0.dev) (flutter/flutter#177132) 2025-10-17 [email protected] Make sure that a MenuAcceleratorLabel doesn't crash in 0x0 environment (flutter/flutter#176646) 2025-10-16 [email protected] Make sure that a NavigationRail doesn't crash in 0x0 environment (flutter/flutter#177022) 2025-10-16 [email protected] Make sure that a SubmenuButton doesn't crash in 0x0 environment (flutter/flutter#176535) 2025-10-16 [email protected] Fix typo in ButtonBar documentation (flutter/flutter#177078) 2025-10-16 [email protected] Make sure that an InkResponse doesn't crash in 0x0 environment (flutter/flutter#175426) 2025-10-16 [email protected] [Gradle 9] Resolve Gradle 9 Deprecations in flutter/flutter part 1 (flutter/flutter#176865) 2025-10-16 [email protected] Revert "Resolve resolve native Flutter dependencies in Android Studio (#167332)" (flutter/flutter#177053) 2025-10-16 [email protected] Manual roll Dart SDK from 2d8e0359a767 to 25b6094026e4 (5 revisions) (flutter/flutter#177109) 2025-10-16 [email protected] Add textfield prop to SearchAnchor (flutter/flutter#174497) 2025-10-16 [email protected] Fix crash when NSAttributedString is passed to insertText on macOS (flutter/flutter#176329) 2025-10-16 [email protected] Correct basque time format (flutter/flutter#177031) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md ...
…lutter#176843) This PR updates selectable region so it dismisses the selection when a tap happens outside of it. This is done through the use of `TapRegion` and it's `onTapOutside` callback. https://github.com/user-attachments/assets/d5678c85-9d04-4c9c-af16-9a09a001c228 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: Renzo Olivares <[email protected]>
This PR updates selectable region so it dismisses the selection when a tap happens outside of it. This is done through the use of
TapRegionand it'sonTapOutsidecallback.Screen.Recording.2025-10-10.at.10.12.42.AM.mov
Pre-launch Checklist
///).