Repository navigation
fix: prefer pointer events over semantics tap for mouse clicks on web - #191529
auto-submit[bot] merged 6 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the ClickDebouncer in the Flutter Web engine to flush queued pointer events and drop the click event for mouse-generated clicks, allowing mouse gestures to be recognized precisely instead of using SemanticsAction.tap. Corresponding tests have been updated to use touch events, and a regression test has been added. Feedback is provided to add a defensive check in _isDebouncingMouseEvents to prevent a potential StateError when accessing the first element of an empty queue.
| bool get _isDebouncingMouseEvents { | ||
| final List<ui.PointerData>? data = _state?.queue.first.data; | ||
| return data != null && data.isNotEmpty && data.first.kind == ui.PointerDeviceKind.mouse; | ||
| } |
There was a problem hiding this comment.
To prevent potential StateError crashes when accessing .first on an empty queue, it is safer to use defensive programming and check if the queue is null or empty before accessing its elements.
| bool get _isDebouncingMouseEvents { | |
| final List<ui.PointerData>? data = _state?.queue.first.data; | |
| return data != null && data.isNotEmpty && data.first.kind == ui.PointerDeviceKind.mouse; | |
| } | |
| bool get _isDebouncingMouseEvents { | |
| final queue = _state?.queue; | |
| if (queue == null || queue.isEmpty) { | |
| return false; | |
| } | |
| final List<ui.PointerData>? data = queue.first.data; | |
| return data != null && data.isNotEmpty && data.first.kind == ui.PointerDeviceKind.mouse; | |
| } |
flutter-zl
left a comment
There was a problem hiding this comment.
Thanks for working on this, the #188859 problem is real and the patch is nice and small.
One concern before it can land: on Chrome, screen reader activation also produces mouse pointer events. Blink sends pointerdown, pointerup and click with pointerType: 'mouse' at the element center, all in one task. That sequence
now takes the new mouse branch, so the click is dropped and nodes that rely on SemanticsAction.tap, such as Semantics(onTap: ...) with no gesture detector underneath, stop responding.
I verified it with VoiceOver on macOS Chrome:
- Before: https://flutter-demo-82-before.web.app
- After, with this PR: https://flutter-demo-82-after.web.app
Press VO+U, pick "Semantics only button" from the Buttons list, then press VO+Space. The counter goes up on the before app but stays at 0 on the after app.
It would also be great to add a test for the one-task mouse sequence that still expects SemanticsAction.tap.
|
@flutter-zl Good catch, thanks for the repro. Updated the PR so the mouse branch is skipped when |
Chrome turns screen reader activation into mouse pointerdown, pointerup and click events dispatched in one task. Only flush pointer events for mouse clicks once debouncing has started, which happens after the pointerdown's task, so screen reader activation keeps producing SemanticsAction.tap.
50e3f8f to
0792f49
Compare
flutter-zl
left a comment
There was a problem hiding this comment.
Thanks for the quick update, this looks good to me.
I rebuilt my demos on the new head and verified with VoiceOver on macOS Chrome: activating "Semantics only button" now counts up on https://flutter-demo-82-after.web.app, so screen reader taps on Semantics(onTap: ...) nodes work again. A physical mouse click is flushed as pointer events, matching the behavior with semantics off.
Roll Flutter from fcfb4f203eae to 934720261be0 (38 revisions) flutter/flutter@fcfb4f2...9347202 2026-11-03 [email protected] Roll Skia from 5bceaa033999 to c733ad3e37cb (5 revisions) (flutter/flutter#195872) 2026-11-03 [email protected] Roll Skia from 34d2bf479385 to 5bceaa033999 (9 revisions) (flutter/flutter#195864) 2026-11-03 [email protected] fix: prefer pointer events over semantics tap for mouse clicks on web (flutter/flutter#191529) 2026-11-03 [email protected] [VPAT] Expose a semantics text field value while it is not being edited (flutter/flutter#195445) 2026-11-03 [email protected] [Impeller] Make a GL context current before constructing an AiksContext without a surface (flutter/flutter#195629) 2026-11-03 [email protected] [flutter_tools] Eliminate globals.dart from project.dart (flutter/flutter#194066) 2026-11-03 [email protected] [tool] Complete context runner elimination and migrate AppContext usages to ToolContext (flutter/flutter#190797) 2026-11-03 [email protected] [web] Unify font collections between CanvasKit and Skwasm (flutter/flutter#195539) 2026-11-03 [email protected] [Android] Replay predictive back state for retained engines (flutter/flutter#192286) 2026-11-03 [email protected] [vulkan]: Adds depth/stencil synchronization for subpasses (flutter/flutter#195689) 2026-11-03 [email protected] [web] Honor requested upscaling in instantiateImageCodecWithSize and ImageDescriptor.instantiateCodec (flutter/flutter#195836) 2026-11-03 [email protected] Roll Skia from 866b61ca92c9 to 34d2bf479385 (9 revisions) (flutter/flutter#195848) 2026-11-03 [email protected] Roll Fuchsia Test Scripts from xl1LWBuppzu-BnOzV... to B6zarykZp6PKWRueg... (flutter/flutter#195847) 2026-11-03 [email protected] Remove unused ffi (flutter/flutter#195710) 2026-11-02 [email protected] Revert: [Android] Let the raster thread use every core of a chip without little cores (flutter/flutter#195838) 2026-11-02 [email protected] Run 10 impeller iOS benchmarks as one devicelab task (flutter/flutter#195661) 2026-11-02 [email protected] Use named uniforms instead of setFloat in StretchEffect (flutter/flutter#195372) 2026-11-02 [email protected] [flutter_tools] Report when the browser disconnects during a web test run (flutter/flutter#192198) 2026-11-02 [email protected] fix: Resolve memory leak in image_list example (flutter/flutter#189526) 2026-11-02 [email protected] Only run the common local engine configurations in presubmit (flutter/flutter#195688) 2026-11-02 [email protected] Update gcloud_version to version [email protected] (flutter/flutter#195769) 2026-11-02 [email protected] [Impeller] Order the Vulkan copy-texture-to-texture layout transition after the copy (flutter/flutter#195814) 2026-11-02 [email protected] [Impeller] Order the Vulkan ResizeTexture layout transition after its blit (flutter/flutter#195721) 2026-11-02 [email protected] [web] Fix CanvasKit fragment shader uniforms being freed or mutated after recording (flutter/flutter#195718) 2026-11-02 [email protected] Roll Fuchsia Linux SDK from MqypI6RNyVZ_FLUE-... to T_-6-6X1lDMghhAvX... (flutter/flutter#195809) 2026-11-02 [email protected] [Android] Log why Impeller falls back to OpenGLES (flutter/flutter#194764) 2026-11-02 [email protected] Roll Skia from 3f35fb6e591b to 866b61ca92c9 (11 revisions) (flutter/flutter#195802) 2026-11-02 [email protected] Roll Skia from 3f35fb6e591b to 866b61ca92c9 (11 revisions) (flutter/flutter#195802) 2026-11-02 [email protected] Only run the common local engine configurations in presubmit (flutter/flutter#195688) 2026-11-02 [email protected] Balance tool_integration_tests subshards by expected duration (flutter/flutter#195628) 2026-11-02 [email protected] [web_paragraph] Map code units without a cluster to a cluster (flutter/flutter#195384) 2026-11-02 [email protected] [web] Hide synthetic autofill inputs from assistive technology (flutter/flutter#195618) 2026-11-02 [email protected] Roll Packages from 3742a4e to 3f34a6f (8 revisions) (flutter/flutter#195795) 2026-11-02 [email protected] [web] Unskip web tests that pass on CanvasKit and Skwasm: platform_view_test, route_notification_messages_test (flutter/flutter#195605) 2026-11-02 [email protected] Roll Skia from e5ad3868b8db to 3f35fb6e591b (1 revision) (flutter/flutter#195789) 2026-11-02 [email protected] Roll Skia from f5215e74aa16 to e5ad3868b8db (3 revisions) (flutter/flutter#195779) 2026-11-02 [email protected] Roll Skia from e43478a2f9f8 to f5215e74aa16 (1 revision) (flutter/flutter#195776) 2026-11-01 [email protected] Roll Fuchsia Linux SDK from kW0A0xxNkqf38ljm4... to MqypI6RNyVZ_FLUE-... (flutter/flutter#195768) If this roll has caused a breakage, revert this CL and set the roller to dry run mode 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 ...
This PR fixes
Tap*Detailsreporting the center of the semantics node andPointerDeviceKind.unknownfor mouse clicks on web when semantics is enabled.When semantics is enabled,
ClickDebouncerwithholds the pointer events that lead up to a DOM "click" on a tappable semantics node and forwards the click as aSemanticsAction.tapinstead. That action carries neither a position nor a device kind, so the framework synthesizes the tap at the center of the semantics node withPointerDeviceKind.unknown. For a merged semantics node that also means the tap is attributed to the merged node rather than to whatever was actually under the cursor.The debouncer exists for assistive technologies, which drive taps either through clicks that come with no pointer events at all, or through touch. Neither of those paths is affected by this change: when the withheld pointer events came from a mouse, they are now flushed to the framework and the click is dropped, so mouse gestures are recognized exactly like they are when semantics is off.
Fixes: #188859
Fixes: #146063
Pre-launch Checklist
///).