Skip to content

fix: exclude the map's gesture detector from semantics - #2236

Open
wcjord wants to merge 1 commit into
fleaflet:masterfrom
wcjord:fix/map-tap-semantics-blankets-viewport
Open

fix: exclude the map's gesture detector from semantics#2236
wcjord wants to merge 1 commit into
fleaflet:masterfrom
wcjord:fix/map-tap-semantics-blankets-viewport

Conversation

@wcjord

@wcjord wcjord commented Jul 30, 2026

Copy link
Copy Markdown

Fixes #2176.

The mechanism

MapInteractiveViewer builds RawGestureDetector(gestures: _gestures, ...) and leaves excludeFromSemantics at its default false. _gestures contains a TapGestureRecognizer and a LongPressGestureRecognizer, so Flutter wraps the child in _GestureSemantics and publishes SemanticsAction.tap / longPress on a semantics node covering the detector's whole hit area — i.e. the entire map.

Two consequences once the semantics tree is enabled (SemanticsBinding.instance.ensureSemantics(), or automatically when assistive technology is detected):

1. onTap reports the map centre (#2176). A semantics-driven activation doesn't carry a pointer position, so the framework synthesises one at the node's centre and the recognizer fires with kind: unknown. Since the node is the size of the map, that centre is the map centre — which is exactly the reported symptom, and why kind changes from mouse to unknown. Long-press is affected in principle for the same reason; drag recognizers publish no semantics action, which is why panning/zooming were unaffected and only onTap looked broken.

2. On web it blankets any HtmlElementView over the map. Flutter web gives a tappable semantics node pointer-events: all. Platform views are not part of the semantics tree, so a map-sized tappable node sits above any <iframe> / <video> composited over the map and swallows its mouse events — the embed renders and plays but its own controls never respond (a YouTube embed never even shows its control bar, because it never receives mousemove).

I confirmed (2) in a Flutter web app by measuring document.elementFromPoint over a playing embed: the top element was the map's flt-semantics node, viewport-sized, pointer-events: all, every ancestor correctly none. Setting pointer-events: none on that one node — changing nothing else — immediately restored the embed's full controls.

The change

excludeFromSemantics: true on that RawGestureDetector. The map's gestures are pointer gestures, not semantic actions: a map-sized "tap" target isn't a meaningful screen-reader affordance, and its only current effect is to mis-report the tap position. Markers and other children keep their own semantics, so nothing meaningfully activatable loses a node.

Testing

  • New regression test in test/gestures/map_interactive_viewer_test.dart: with semantics enabled, a bare FlutterMap publishes no tap/longPress semantics action. It fails on master and passes with this change.
  • Full suite green locally (118/118).

Happy to adjust the approach — if you'd rather keep a semantic affordance on the map and fix the position synthesis instead, say so and I'll rework it.

AI disclosure

Per CONTRIBUTING: this was written with AI assistance (Claude). I've reviewed the diagnosis and the diff, ran the tests locally, and take responsibility for the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ensureSemantics() breaks onTap callback on Web platform

1 participant