@@ -428,7 +428,7 @@ void _testEngineSemanticsOwner() {
428428 expectSemanticsTree ('''
429429<sem style="$rootSemanticStyle ">
430430 <sem-c>
431- <sem aria-label="Hello"></sem>
431+ <sem aria-label="Hello" role="text" ></sem>
432432 </sem-c>
433433</sem>''' );
434434
@@ -443,7 +443,7 @@ void _testEngineSemanticsOwner() {
443443 expectSemanticsTree ('''
444444<sem style="$rootSemanticStyle ">
445445 <sem-c>
446- <a aria-label="Hello" role="button" style="display: block;"></a>
446+ <a aria-label="Hello" style="display: block;"></a>
447447 </sem-c>
448448</sem>''' );
449449 expect (existingParent, tree[1 ]! .element.parent);
@@ -2106,6 +2106,89 @@ void _testTappable() {
21062106
21072107 semantics ().semanticsEnabled = false ;
21082108 });
2109+
2110+ // Regression test for: https://github.com/flutter/flutter/issues/134842
2111+ //
2112+ // If the click event is allowed to propagate through the hierarchy, then both
2113+ // the descendant and the parent will generate a SemanticsAction.tap, causing
2114+ // a double-tap to happen on the framework side.
2115+ test ('inner tappable overrides ancestor tappable' , () async {
2116+ semantics ()
2117+ ..debugOverrideTimestampFunction (() => _testTime)
2118+ ..semanticsEnabled = true ;
2119+
2120+ final List <CapturedAction > capturedActions = < CapturedAction > [];
2121+ EnginePlatformDispatcher .instance.onSemanticsActionEvent = (ui.SemanticsActionEvent event) {
2122+ capturedActions.add ((event.nodeId, event.type, event.arguments));
2123+ };
2124+
2125+ final SemanticsTester tester = SemanticsTester (semantics ());
2126+ tester.updateNode (
2127+ id: 0 ,
2128+ isFocusable: true ,
2129+ hasTap: true ,
2130+ hasEnabledState: true ,
2131+ isEnabled: true ,
2132+ isButton: true ,
2133+ rect: const ui.Rect .fromLTRB (0 , 0 , 100 , 50 ),
2134+ children: < SemanticsNodeUpdate > [
2135+ tester.updateNode (
2136+ id: 1 ,
2137+ isFocusable: true ,
2138+ hasTap: true ,
2139+ hasEnabledState: true ,
2140+ isEnabled: true ,
2141+ isButton: true ,
2142+ rect: const ui.Rect .fromLTRB (0 , 0 , 100 , 50 ),
2143+ ),
2144+ ],
2145+ );
2146+ tester.apply ();
2147+
2148+ expectSemanticsTree ('''
2149+ <sem flt-tappable role="button" style="$rootSemanticStyle ">
2150+ <sem-c>
2151+ <sem flt-tappable role="button"></sem>
2152+ </sem-c>
2153+ </sem>
2154+ ''' );
2155+
2156+ // Tap on the outer element
2157+ {
2158+ final DomElement element = tester.getSemanticsObject (0 ).element;
2159+ final DomRect rect = element.getBoundingClientRect ();
2160+
2161+ element.dispatchEvent (createDomMouseEvent ('click' , < Object ? , Object ? > {
2162+ 'clientX' : (rect.left + (rect.right - rect.left) / 2 ).floor (),
2163+ 'clientY' : (rect.top + (rect.bottom - rect.top) / 2 ).floor (),
2164+ }));
2165+
2166+ expect (capturedActions, < CapturedAction > [
2167+ (0 , ui.SemanticsAction .tap, null ),
2168+ ]);
2169+ }
2170+
2171+ // Tap on the inner element
2172+ {
2173+ capturedActions.clear ();
2174+ final DomElement element = tester.getSemanticsObject (1 ).element;
2175+ final DomRect rect = element.getBoundingClientRect ();
2176+
2177+ element.dispatchEvent (createDomMouseEvent ('click' , < Object ? , Object ? > {
2178+ 'bubbles' : true ,
2179+ 'clientX' : (rect.left + (rect.right - rect.left) / 2 ).floor (),
2180+ 'clientY' : (rect.top + (rect.bottom - rect.top) / 2 ).floor (),
2181+ }));
2182+
2183+ // The click on the inner element should not propagate to the parent to
2184+ // avoid sending a second SemanticsAction.tap action to the framework.
2185+ expect (capturedActions, < CapturedAction > [
2186+ (1 , ui.SemanticsAction .tap, null ),
2187+ ]);
2188+ }
2189+
2190+ semantics ().semanticsEnabled = false ;
2191+ });
21092192}
21102193
21112194void _testImage () {
0 commit comments