diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index 2c8ad8a6012df..311b5b77a00c6 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -61,7 +61,9 @@ class Tappable extends RoleManager { // Request focus so that the AT shifts a11y focus to this node. if (semanticsObject.isFlagsDirty && semanticsObject.hasFocus) { - element.focus(); + semanticsObject.owner.addOneTimePostUpdateCallback(() { + element.focus(); + }); } } diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index 3de8054bc9844..e35d5f6e40dc1 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -1628,6 +1628,27 @@ void _testTappable() { semantics().semanticsEnabled = false; }); + + test('focuses on tappable after element has been attached', () async { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + final SemanticsTester tester = SemanticsTester(semantics()); + tester.updateNode( + id: 0, + hasTap: true, + hasEnabledState: true, + isEnabled: true, + isButton: true, + isFocused: true, + rect: const ui.Rect.fromLTRB(0, 0, 100, 50), + ); + tester.apply(); + + expect(flutterViewEmbedder.glassPaneShadow!.activeElement, tester.getSemanticsObject(0).element); + semantics().semanticsEnabled = false; + }); } void _testImage() {