Skip to content

tap has no occlusion/hit-test guard — can succeed against overlay-covered widgets #265

Description

@pbertsch

Tracked internally as FP-10 (https://alphawavesystems.atlassian.net/browse/FP-10).

Problem

_tap/_doubleTap/_longPress in probe_agent/lib/src/executor.dart (~lines 311-402) resolve a selector to an Element via ProbeFinder (which already filters to mounted + current-route + non-zero-size elements — the separate off-screen/scroll-into-view gap is tracked as PT-03 in IMPROVEMENT_TASKS.md) and then interact with it one of two ways:

  1. If element.widget is Semantics, _tryDirectTap walks down to a GestureDetector/InkResponse descendant and calls widget.onTap!() directly — no hit-testing at all.
  2. Otherwise (or if _tryDirectTap finds nothing), a synthetic pointer gesture fires at the element's localToGlobal center via _createGesture.

Neither path checks whether that screen coordinate is actually the topmost hit. So tap can succeed against a button that's mounted, on the current route, and non-zero-size, but visually covered by something a real user's finger would hit first — a modal barrier, a blocking/loading overlay, a snackbar, a bottom sheet, another Stack sibling drawn on top. The test reports success even though a human tester could not have produced that outcome.

Correction to an earlier framing

An earlier proposal was to reorder tap dispatch (hit-tested gesture first, direct-invoke as fallback). That's wrong — see PT-04/PT-05 in IMPROVEMENT_TASKS.md (confirmed: a real hit-tested tap on a Semantics-wrapped widget didn't reliably focus text fields or invoke onTap on buttons/drawer-triggers; _tryDirectTap exists specifically to fix that) and PT-22 (closed — a crash that looked like evidence against the direct-invoke path turned out to be an unrelated, rare Flutter framework race, irrelevant in release builds). No evidence in this repo's own history supports reordering; doing so would reintroduce PT-04/PT-05.

Correct scope

Add an occlusion guard that runs before either tap path fires, independent of invocation order: hit-test the target's screen-space center (e.g. via the current binding's hitTestInView) and confirm the resolved element/RenderObject is actually part of the hit-test result at that point. If occluded, tap should fail loudly (e.g. "target is covered by <widget>, not tappable") instead of silently proceeding via _tryDirectTap's bypass.

Suggested implementation

  • New helper, e.g. _isOccluded(Element element, Offset point), in executor.dart or finder.dart (alongside _isVisible).
  • Called from _tap, _doubleTap, _longPress before either invocation path.
  • Open question: hard-fail vs. warn-and-proceed — given PT-02's theme (executor silently no-ops on malformed input) and this project's general direction toward loud failures, hard-fail seems consistent, but needs a maintainer call.
  • Test plan: Dart widget test in probe_agent/test/ — a button under a Stack covered by an opaque sibling / ModalBarrier, asserting tap fails rather than invoking the covered button's onTap.

Files likely touched

  • probe_agent/lib/src/executor.dart (_tap, _doubleTap, _longPress, new occlusion helper)
  • probe_agent/lib/src/finder.dart (if the occlusion check lives here instead, alongside _isVisible)
  • probe_agent/test/ (new coverage)
  • IMPROVEMENT_TASKS.md (candidate PT-28, following existing convention)

Related but distinct

PT-03 (scroll-to-element / off-screen targeting) covers the target not being in the viewport at all. This issue covers the target being in the viewport but covered by something else at that exact point. Same underlying principle (tap should only succeed where a real user's tap would), different code paths, likely different PRs.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions