From f37632faa4a52ad538c1ea2cf199e176ac347e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Thu, 16 Jul 2026 09:57:49 +0300 Subject: [PATCH 1/4] fix(anchor): make the whole WAnchor bounds tappable via translucent hit-test WAnchor's GestureDetector used the default HitTestBehavior.deferToChild, so it only received a pointer when a painted child sat under the tap point. An anchor wrapping transparent content (a settings row with its label on one side, a link, an icon row) then ignored taps that landed on its empty regions: a real user tapping the blank part of the row, and every centre-of-element tap from a driver (dusk / Playwright), silently did nothing. The existing tap tests all worked around this with an opaque Container child ("Ensure hit-testable"). Set behavior: HitTestBehavior.translucent so the whole anchor bounds are the hit target, matching the whole-box tap WInput and WPopover already use. Adds a regression test that taps a transparent region of the anchor and asserts onTap fires. --- lib/src/widgets/w_anchor.dart | 10 ++++++++++ test/widgets/w_anchor_test.dart | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/src/widgets/w_anchor.dart b/lib/src/widgets/w_anchor.dart index a53cb2c3..1db7f157 100644 --- a/lib/src/widgets/w_anchor.dart +++ b/lib/src/widgets/w_anchor.dart @@ -198,6 +198,16 @@ class _WAnchorState extends State { // Only wrap with GestureDetector if there are actual gesture callbacks if (hasGestures) { innerChild = GestureDetector( + // Translucent so the whole anchor bounds are tappable, not only the + // opaque descendants. The GestureDetector defaults to + // `HitTestBehavior.deferToChild`, which only receives a pointer when a + // painted child sits under it; an anchor wrapping transparent content + // (a settings row with text on one side, a link, an icon row) then + // ignored taps that landed on its empty regions. Real users tapping the + // blank part of a row, and every centre-of-element tap from a driver + // (dusk / Playwright), silently did nothing. Translucent matches the + // whole-box tap target WInput and WPopover already use. + behavior: HitTestBehavior.translucent, onTap: widget.isDisabled ? null : widget.onTap, onLongPress: widget.isDisabled ? null : widget.onLongPress, onDoubleTap: widget.isDisabled ? null : widget.onDoubleTap, diff --git a/test/widgets/w_anchor_test.dart b/test/widgets/w_anchor_test.dart index 56489de1..f0e6aa77 100644 --- a/test/widgets/w_anchor_test.dart +++ b/test/widgets/w_anchor_test.dart @@ -208,6 +208,40 @@ void main() { expect(tapped, isTrue); }); + testWidgets( + 'onTap fires when tapping a transparent region of the anchor', + (tester) async { + // A settings-row-style anchor: the interactive content (a label) sits + // on the left, leaving the centre/right transparent. With the + // GestureDetector defaulting to deferToChild, a tap on the transparent + // centre missed the gesture entirely, so a real user tapping the blank + // part of a row, and every centre-of-element tap from a driver + // (dusk / Playwright), silently did nothing. The whole anchor bounds + // must be tappable. + bool tapped = false; + await tester.pumpWidget( + wrapWithTheme( + WAnchor( + onTap: () => tapped = true, + child: const SizedBox( + width: 300, + height: 60, + child: Align( + alignment: Alignment.centerLeft, + child: Text('Dark'), + ), + ), + ), + ), + ); + + // Tap the centre of the anchor: a transparent region with no opaque + // child under it (the label is pinned to the far left). + await tester.tapAt(tester.getCenter(find.byType(WAnchor))); + await tester.pump(); + expect(tapped, isTrue); + }); + testWidgets('onLongPress callback fires', (tester) async { bool pressed = false; await tester.pumpWidget( From 2e59167dde536aef6764f47244af97a752771125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Sat, 18 Jul 2026 02:19:51 +0300 Subject: [PATCH 2/4] docs(changelog): note the WAnchor translucent hit-test fix --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d142e3b0..9243f01c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0. ## [Unreleased] +### Fixed + +- **`WAnchor` now hit-tests its whole bounds via `HitTestBehavior.translucent`.** The inner `GestureDetector` used the default `HitTestBehavior.deferToChild`, so it only fired when a painted child sat under the exact tap point. An anchor wrapping transparent content (a settings row, a checkbox row, a link with padding) ignored taps that landed on its empty regions, including the element centre that automated drivers and centred pointer events target. It now behaves like `WInput`/`WPopover`, which already use whole-box hit-testing, so the full anchor rectangle is tappable. (`lib/src/widgets/w_anchor.dart`; covered by `test/widgets/w_anchor_test.dart`) + ## [1.2.0] - 2026-07-08 ### Changed From 67e1ec4e5ed9fa6e90dff83afc9dfc0aef5f32d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Sat, 18 Jul 2026 02:46:34 +0300 Subject: [PATCH 3/4] style: dart format --- test/widgets/w_anchor_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/widgets/w_anchor_test.dart b/test/widgets/w_anchor_test.dart index f0e6aa77..bc1beacc 100644 --- a/test/widgets/w_anchor_test.dart +++ b/test/widgets/w_anchor_test.dart @@ -208,8 +208,7 @@ void main() { expect(tapped, isTrue); }); - testWidgets( - 'onTap fires when tapping a transparent region of the anchor', + testWidgets('onTap fires when tapping a transparent region of the anchor', (tester) async { // A settings-row-style anchor: the interactive content (a label) sits // on the left, leaving the centre/right transparent. With the From 80cfc5f0c6f2eb260f8298d75b1ba5ddeed48711 Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Sat, 18 Jul 2026 03:18:23 +0300 Subject: [PATCH 4/4] docs(changelog): drop bold opening to match the changelog house style Every 1.2.0 entry opens in plain text; the Unreleased WAnchor entry used a bold lead-in that deviated from that tone. Flagged by CodeRabbit. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9243f01c..a949dcc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0. ### Fixed -- **`WAnchor` now hit-tests its whole bounds via `HitTestBehavior.translucent`.** The inner `GestureDetector` used the default `HitTestBehavior.deferToChild`, so it only fired when a painted child sat under the exact tap point. An anchor wrapping transparent content (a settings row, a checkbox row, a link with padding) ignored taps that landed on its empty regions, including the element centre that automated drivers and centred pointer events target. It now behaves like `WInput`/`WPopover`, which already use whole-box hit-testing, so the full anchor rectangle is tappable. (`lib/src/widgets/w_anchor.dart`; covered by `test/widgets/w_anchor_test.dart`) +- `WAnchor` now hit-tests its whole bounds via `HitTestBehavior.translucent`. The inner `GestureDetector` used the default `HitTestBehavior.deferToChild`, so it only fired when a painted child sat under the exact tap point. An anchor wrapping transparent content (a settings row, a checkbox row, a link with padding) ignored taps that landed on its empty regions, including the element centre that automated drivers and centred pointer events target. It now behaves like `WInput`/`WPopover`, which already use whole-box hit-testing, so the full anchor rectangle is tappable. (`lib/src/widgets/w_anchor.dart`; covered by `test/widgets/w_anchor_test.dart`) ## [1.2.0] - 2026-07-08