[web] Fix MenuAnchor dismiss when semantics enabled#183093
Conversation
…er events for non-merged nodes.
…scendants parameter.
There was a problem hiding this comment.
Code Review
This pull request addresses an issue where MenuAnchor does not dismiss on an outside click when semantics are enabled. A new mergesDescendants flag is introduced and propagated through the semantics system. This flag is used in ClickDebouncer to conditionally flush pointer events for non-merged nodes, which allows TapRegionSurface to detect outside taps. For merged nodes, SemanticsAction.tap is still used to preserve existing behavior. The changes also include updates to tests to cover the new logic. My feedback is focused on improving documentation for the newly introduced parameters.
chunhtai
left a comment
There was a problem hiding this comment.
Can you explain a bit more about the exact repro setup?
is it because the there is an area that it should hit inside a big semantics node, but instead swallowed by the semantics tap? how does the rect looks like in this cases?
| // There's a pending queue of pointer events. Prefer sending the tap action | ||
| // instead of pointer events, because the pointer events may not land on the | ||
| // combined semantic node and miss the click/tap. | ||
| if (isMergedNode && isListening) { |
There was a problem hiding this comment.
merge node may not be the only cases this can be true,
you can have a gesture detector under a Semantics(container: true) which has smaller rect but merge up to the bigger container
There was a problem hiding this comment.
Good catch. I broadened the flag from mergesDescendants to absorbedChildSemantics. Instead of only checking _mergeAllDescendantsIntoThisNode, the framework now sets absorbedChildSemantics = true whenever absorbAll encounters a child SemanticsConfiguration that hasBeenAnnotated and has an onTap handler. This covers both MergeSemantics and Semantics(container: true) cases where a smaller tappable child's tap action gets absorbed into a larger container node, causing a rect mismatch.
In the engine, ClickDebouncer.onClick now checks absorbedChildSemantics instead of isMergedNode. When true, it sends SemanticsAction.tap to avoid coordinate miss-hits. When false, it flushes pointer events so TapRegionSurface can detect outside taps and close MenuAnchor.
The repro setup is the demo app linked in the PR description. A |
|
@chunhtai should this be considered a bug in |
|
Thanks for the info, yeah I agree this is something that probably should be fixed in TapRegionSurface. This will be tricky to implement though since semantics action and gesture rect are two separate channel. there isn't a good way to hook TapRegionSurface to the semantics action without actually participate in the semantics tree. |
|
Yeah, that's the bug 🙂 As a first step, SemanticsBinding.instance.addSemanticsActionListener(...);Then, it needs to decide if the received tap was inside or outside. This is the part I'm not sure how to implement. Any ideas @chunhtai ? I'm assuming we can't use |
|
The only way i can think of is to use |
| /// the view has no semantics owner, or the node cannot be found. Callers | ||
| /// should only invoke this in response to a semantics action, in which case | ||
| /// all three lookups are expected to succeed. | ||
| ui.Rect? semanticsNodeGlobalRect(int viewId, int nodeId) => null; |
There was a problem hiding this comment.
nit: rename this getRectOfSemanticsNodeInViewCoordinates.
|
autosubmit label was removed for flutter/flutter/183093, because This PR has not met approval requirements for merging. Changes were requested by {mdebbar}, please make the needed changes and resubmit this PR.
|
…11713) Manual roll requested by bmparr@google.com flutter/flutter@23f6f58...0541913 2026-05-14 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Windows] Propagate the enabled accessibility state (#184501)" (flutter/flutter#186492) 2026-05-13 srawlins@google.com [dev] Use super parameters in missed spots (flutter/flutter#186193) 2026-05-13 loic.peron@inetum.com [Windows] Propagate the enabled accessibility state (flutter/flutter#184501) 2026-05-13 matt.boetger@gmail.com [flutter_tool] filter out MotionEvent-JNI warning spam from logcat (#174783) (flutter/flutter#186079) 2026-05-13 engine-flutter-autoroll@skia.org Roll Packages from 93cbed6 to 2ec2236 (1 revision) (flutter/flutter#186464) 2026-05-13 mdebbar@google.com [web] Fix untriaged issues link label (flutter/flutter#186465) 2026-05-13 bdero@google.com [Impeller] Namespace user-supplied shaders to prevent entrypoint collisions (flutter/flutter#186332) 2026-05-13 1063596+reidbaker@users.noreply.github.com [flutter_tools] Migrate detectLowCompileSdkVersionOrNdkVersion to AGP task (flutter/flutter#184731) 2026-05-13 jason-simmons@users.noreply.github.com Update the Flutter Gallery web app template files to support running with Wasm (flutter/flutter#186268) 2026-05-13 jason-simmons@users.noreply.github.com [web] Use heap allocation for buffers that would consume too much space on the Wasm stack (flutter/flutter#186228) 2026-05-13 engine-flutter-autoroll@skia.org Roll Skia from 56ca5896c0d9 to 27f7bba22600 (3 revisions) (flutter/flutter#186444) 2026-05-13 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from z7ICmPtn4hspu02zk... to y6uQHA5xUN83IF395... (flutter/flutter#186442) 2026-05-13 engine-flutter-autoroll@skia.org Roll Skia from 6385958d2feb to 56ca5896c0d9 (1 revision) (flutter/flutter#186441) 2026-05-13 engine-flutter-autoroll@skia.org Roll Dart SDK from 9576691c37d8 to 8e30b88e4d5a (1 revision) (flutter/flutter#186429) 2026-05-13 engine-flutter-autoroll@skia.org Roll Skia from 77a21bc723dc to 6385958d2feb (9 revisions) (flutter/flutter#186428) 2026-05-13 164032450+AlexEduV@users.noreply.github.com Docs/improving docs for semantics UI lib (flutter/flutter#186125) 2026-05-12 jason-simmons@users.noreply.github.com [Tool] Support glob patterns when parsing workspaces in FlutterProject (flutter/flutter#185715) 2026-05-12 nico.reiab@gmail.com docs: fix overriden -> overridden in MediaQueryData dartdoc (flutter/flutter#186323) 2026-05-12 brackenavaron@gmail.com [Test cross imports] No material in `test/foundation`, `test/gestures`, `test/semantics`, `test/services` (flutter/flutter#186144) 2026-05-12 nico.reiab@gmail.com docs: fix "tha" -> "that" typo in widget_inspector_test comment (flutter/flutter#186322) 2026-05-12 nico.reiab@gmail.com docs: Fix doubled-word typos in framework dartdoc (flutter/flutter#186319) 2026-05-12 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#186418) 2026-05-12 30870216+gaaclarke@users.noreply.github.com Bumped required mediatek vender sdk version. (flutter/flutter#186405) 2026-05-12 magder@google.com Make DeepLinkJsonFromManifestTask Gradle task build cacheable (flutter/flutter#185903) 2026-05-12 66727653+ishaq2321@users.noreply.github.com Harden dev tooling scripts against command injection and log leaks (flutter/flutter#186076) 2026-05-12 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#186274) 2026-05-12 bdero@google.com [Flutter GPU] Allow allocating multi-mip textures and overwriting specific (mip, slice) levels (flutter/flutter#185890) 2026-05-12 zhongliu88889@gmail.com [web] Fix MenuAnchor dismiss when semantics enabled (flutter/flutter#183093) 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 bmparr@google.com,stuartmorgan@google.com 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
Fixes #167487
Problem
When semantics are enabled,
MenuAnchordoes not close when clicking outside it.ClickDebouncerwas converting all debounced clicks toSemanticsAction.tap, which bypasses the pointer event pipeline.TapRegionSurfacenever saw aPointerDownEvent, soonTapOutsidewas never called and the menu stayed open.Fix
TapRegionSurfacenow registers a listener withSemanticsBinding.addSemanticsActionListenerto interceptSemanticsAction.tapandSemanticsAction.longPressevents. On each action, it hit-tests the render tree at the tapped node's center and callsonTapOutside/onTapInsidedirectly. The inside/outside classification is extracted into a shared_classifyRegionsmethod used by both the pointer and semantics paths.ClickDebouncer.onClickalso callsstopPropagation()before flushing pointer events for theisListening: falsecase, preventing a duplicate browser click.Note:
consumeOutsideTapscannot suppress semantics action propagation today. A follow-up API change similar toWidgetsBindingObserver.handleStartBackGesturewould be needed. See the TODO in_handleSemanticsAction.Demo
Before change
https://flutter-demo-14-before.web.app
After change
https://flutter-demo-14-after.web.app
Steps: click Open Menu → click Button A outside the menu.
Before: button fires but menu stays open.
After: button fires and menu closes.