Repository navigation
[web] Skip non-focusable containers in Focusable.focusAsRouteDefault - #194138
Conversation
…teDefault
When a route (`SemanticsFlag.scopesRoute`) opens on Flutter Web without an explicit `autofocus` target, `SemanticRouteBase._setDefaultFocus()` traverses descendants in pre-order calling `semanticRole.focusAsRouteDefault()` until a node returns `true`.
Previously, `Focusable.focusAsRouteDefault()` unconditionally called `owner.element.focusWithoutScroll()` and returned `true` without checking `semanticsObject.isFocusable`. Because `Focusable.update()` only attaches a `tabindex="0"` attribute when `semanticsObject.isFocusable` is `true`, any non-focusable container role that mixed in `Focusable` (`SemanticScrollable`, `SemanticForm`, `SemanticList`, `SemanticListItem`, `SemanticTable`, `SemanticRow`, `SemanticCell`, `SemanticHeader`, `SemanticRadioGroup`, `SemanticTabList`, `SemanticTabPanel`, `SemanticImage`, `SemanticsLoadingSpinner`, and landmark roles) executed a silent browser DOM no-op (`focus()` on an element without `tabindex`) and returned `true`, halting the route's default-focus search before reaching any inner heading, text, or interactive control.
Guard `Focusable.focusAsRouteDefault()` with `if (!semanticsObject.isFocusable) { return false; }` so non-focusable semantics containers are skipped and `_setDefaultFocus()` descends into their children.
There was a problem hiding this comment.
Code Review
This pull request updates focusAsRouteDefault in focusable.dart to return early if the semantics object is not focusable, and adds test coverage for skipping non-focusable semantic containers (scrollables, forms, lists, and tables) to focus on inner descendants. The review feedback notes that the new tests modify the global singleton EnginePlatformDispatcher.instance.onSemanticsActionEvent without restoring its original value, which can cause test flakiness, and suggests using addTearDown to properly clean up the state.
…default focus tests
flutter-zl
left a comment
There was a problem hiding this comment.
LGTM. Thanks for tracking this down.
I also tried it in a small app, built on the same engine with and without this change. Each demo has a plain dialog, a scrollable dialog using AlertDialog(scrollable: true), and a drawer with a ListView. A status line shows which element has DOM focus.
…#13220) Manual roll Flutter from d03768eef055 to 4249ccbc18a3 (158 revisions) Manual roll requested by [email protected] flutter/flutter@d03768e...4249ccb 2026-10-12 [email protected] [AGP 9.1.0 Migration #8] Unify add-to-app module wiring on the variant API (flutter/flutter#193717) 2026-10-12 [email protected] [master] Update Flutter DEPS to Dart 5933cb486b4aef2f8cf6b9f89a5651c0d5b0955e (flutter/flutter#193982) 2026-10-12 [email protected] [Android] Fix use-after-free of SurfaceControl.Transaction in HCPP (flutter/flutter#192969) 2026-10-12 [email protected] [windows] Add OpenGL compositor tests against ANGLE at D3D11 feature level 10_0 (flutter/flutter#194114) 2026-10-12 [email protected] Fix HDR image packing for ios (flutter/flutter#192526) 2026-10-12 [email protected] Bump customer tests to pick up material_ui and cupertino_ui (flutter/flutter#194236) 2026-10-12 [email protected] Remove no-shuffle from text_field_test.dart (flutter/flutter#193550) 2026-10-12 [email protected] [web] Clip semantics DOM text labels to stop stealing taps (flutter/flutter#194011) 2026-10-12 [email protected] Roll Skia from 9b6ceba4728d to 17f6986c226c (6 revisions) (flutter/flutter#194232) 2026-10-12 [email protected] [web] Skip non-focusable containers in Focusable.focusAsRouteDefault (flutter/flutter#194138) 2026-10-12 [email protected] [web_paragraph] Honor kTextHeightNone in TextStyle and ParagraphStyle (flutter/flutter#193733) 2026-10-12 [email protected] Roll Skia from c88b49d1d6c7 to 9b6ceba4728d (4 revisions) (flutter/flutter#194226) 2026-10-12 [email protected] Migrate remaining test to macOS 26 (flutter/flutter#193825) 2026-10-12 [email protected] Roll Dart SDK from 047ca322aa9c to 221363557163 (1 revision) (flutter/flutter#194219) 2026-10-12 [email protected] [flutter_tools] Include dev_dependencies hooks for release integration tests (flutter/flutter#193436) 2026-10-12 [email protected] Roll Skia from d8afad1b1829 to c88b49d1d6c7 (1 revision) (flutter/flutter#194198) 2026-10-12 [email protected] Roll Skia from b23ddbff1265 to d8afad1b1829 (4 revisions) (flutter/flutter#194195) 2026-10-11 [email protected] Roll Skia from a168863e44ec to b23ddbff1265 (1 revision) (flutter/flutter#194185) 2026-10-11 [email protected] Roll Skia from 0c1d93abfa04 to a168863e44ec (1 revision) (flutter/flutter#194181) 2026-10-11 [email protected] iOS: Re-enable rendering on willEnterForeground (flutter/flutter#191350) 2026-10-11 [email protected] Roll Skia from 93c9d3ffda25 to 0c1d93abfa04 (1 revision) (flutter/flutter#194179) 2026-10-11 [email protected] Remove more instances of deprecated `withOpacity` (flutter/flutter#193559) 2026-10-11 [email protected] Fix another `parameter_assignments` warning (flutter/flutter#193669) 2026-10-11 [email protected] Roll Skia from 3136abef8f58 to 93c9d3ffda25 (1 revision) (flutter/flutter#194169) 2026-10-11 [email protected] [Impeller] Guard the sampler library caches with a mutex (flutter/flutter#193738) 2026-10-11 [email protected] Roll Dart SDK from e9c89888bcc1 to 047ca322aa9c (1 revision) (flutter/flutter#194157) 2026-10-10 [email protected] Roll Dart SDK from 787baa8aae78 to e9c89888bcc1 (2 revisions) (flutter/flutter#194141) 2026-10-10 [email protected] Fix OverlayPortal layout in shrink-wrapped overlays (flutter/flutter#193079) 2026-10-10 [email protected] Roll Skia from 353076e27b1c to 3136abef8f58 (1 revision) (flutter/flutter#194130) 2026-10-10 [email protected] Roll Dart SDK from 88a07589b1ff to 787baa8aae78 (1 revision) (flutter/flutter#194124) 2026-10-10 [email protected] Route windowing view lookups through WidgetsBinding (flutter/flutter#192666) 2026-10-10 [email protected] Roll Dart SDK from 2a06cab34715 to 88a07589b1ff (1 revision) (flutter/flutter#194117) 2026-10-10 [email protected] Roll Skia from f9043b7aa798 to 353076e27b1c (2 revisions) (flutter/flutter#194115) 2026-10-10 [email protected] Refactored PipelineLibraryGLES (flutter/flutter#193985) 2026-10-09 [email protected] add shorthands for scroll physics subclasses in framework (flutter/flutter#193082) 2026-10-09 [email protected] Roll Dart SDK from ba889e3cee8a to 2a06cab34715 (1 revision) (flutter/flutter#194105) 2026-10-09 [email protected] Roll Skia from 5ea03d8a53a6 to f9043b7aa798 (2 revisions) (flutter/flutter#194099) 2026-10-09 [email protected] Add docs for using bb and led with LUCI (flutter/flutter#193859) 2026-10-09 [email protected] Roll Skia from e329bb617820 to 5ea03d8a53a6 (5 revisions) (flutter/flutter#194087) 2026-10-09 [email protected] Roll Packages from 54706bd to d8d9e97 (5 revisions) (flutter/flutter#194080) 2026-10-09 [email protected] Move the Vulkan wrapped-texture helper out of the GPU surface (flutter/flutter#193001) 2026-10-09 [email protected] Roll Skia from 3e54bcf4c963 to e329bb617820 (1 revision) (flutter/flutter#194078) 2026-10-09 [email protected] Roll Dart SDK from 23f0308f9af8 to ba889e3cee8a (2 revisions) (flutter/flutter#194077) 2026-10-09 [email protected] Bump dartdoc to 9.0.11 (flutter/flutter#194076) ...
…lutter#194138) Guards `Focusable.focusAsRouteDefault()` with `if (!semanticsObject.isFocusable) return false;` so non-focusable container roles (`SemanticScrollable`, `SemanticForm`, `SemanticList`, `SemanticTable`) do not swallow initial route focus when dialogs or drawers open on Web. Verified: `felt test --suite chrome-dart2js-canvaskit-engine test/engine/semantics/semantics_test.dart` (271 passed). Part of flutter#138371 (related to flutter#122876, flutter#120894). - `engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart`: Returns `false` from `focusAsRouteDefault()` when `!semanticsObject.isFocusable` (matching `Focusable.update()`, which only sets `tabindex="0"` when `isFocusable` is `true`). - `engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart`: Resets `onSemanticsActionEvent` in suite `tearDown` and verifies that `SemanticRoute` skips non-focusable `SemanticScrollable`, `SemanticForm`, `SemanticList`, and `SemanticTable` containers to focus the first inner focusable descendant. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
Guards
Focusable.focusAsRouteDefault()withif (!semanticsObject.isFocusable) return false;so non-focusable container roles (SemanticScrollable,SemanticForm,SemanticList,SemanticTable) do not swallow initial route focus when dialogs or drawers open on Web. Verified:felt test --suite chrome-dart2js-canvaskit-engine test/engine/semantics/semantics_test.dart(271 passed). Part of #138371 (related to #122876, #120894).engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart: ReturnsfalsefromfocusAsRouteDefault()when!semanticsObject.isFocusable(matchingFocusable.update(), which only setstabindex="0"whenisFocusableistrue).engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart: ResetsonSemanticsActionEventin suitetearDownand verifies thatSemanticRouteskips non-focusableSemanticScrollable,SemanticForm,SemanticList, andSemanticTablecontainers to focus the first inner focusable descendant.Pre-launch Checklist
///).