Skip to content

Wrap routing in FocusScope in WidgetsApp.router - #192601

Merged
auto-submit[bot] merged 7 commits into
flutter:masterfrom
kevmoo:issue-187561
Sep 17, 2026
Merged

auto-submit[bot] merged 7 commits into
flutter:masterfrom
kevmoo:issue-187561

Conversation

@kevmoo

@kevmoo kevmoo commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

In #109702, Navigator's internal FocusScopeNode was removed and hoisted into WidgetsApp, but only inside the else if (_usesNavigator) branch. As a result, apps using WidgetsApp.router (including MaterialApp.router and CupertinoApp.router with RouterConfig or RouterDelegate) lacked the enclosing FocusScope(debugLabel: 'Navigator Scope', autofocus: true).

On Flutter Web with semantics enabled (ensureSemantics()), when widgets such as Autocomplete show an OverlayPortal, the semantics compiler reparents the route node under a newly synthesized container node. Without an enclosing FocusScope, moving the route's semantics node detaches it from its previous DOM parent, triggering a native browser blur event on any focused <input>. The text field loses focus immediately after typing a single character, causing the options overlay to dismiss.

This change wraps routing in FocusScope whenever routing != null in WidgetsApp, ensuring consistent focus scope hierarchy across _usesNavigator, _usesRouterWithConfig, and _usesRouterWithDelegates.

Fixes #187561

Pre-launch Checklist

In flutter#109702, Navigator's internal FocusScopeNode was removed and hoisted
into WidgetsApp, but only inside the `else if (_usesNavigator)` branch.
As a result, apps using `WidgetsApp.router` (including `MaterialApp.router`
and `CupertinoApp.router` with `RouterConfig` or `RouterDelegate`) lacked
the enclosing `FocusScope(debugLabel: 'Navigator Scope', autofocus: true)`.

On Flutter Web with semantics enabled, when widgets such as
`Autocomplete` display an `OverlayPortal`, the semantics compiler
reparents the route node under a newly synthesized container node.
Without an enclosing `FocusScope`, moving the route's semantics node
detaches it from its previous DOM parent, triggering a native browser
blur event on the focused `<input>`. The text field loses focus
immediately after typing a single character, causing the options overlay
to dismiss.

This change wraps `routing` in `FocusScope` whenever `routing != null` in
`WidgetsApp`, ensuring consistent focus scope hierarchy across
`_usesNavigator`, `_usesRouterWithConfig`, and `_usesRouterWithDelegates`.

Fixes flutter#187561
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Sep 11, 2026
@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label Sep 11, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies WidgetsApp to wrap the routing widget in a FocusScope regardless of whether it uses a Navigator or a Router. Corresponding tests have been added to verify that WidgetsApp.router, WidgetsApp.router with routerConfig, and WidgetsApp with a navigator correctly wrap their routing in a FocusScope. There are no review comments, and I have no feedback to provide.

- Assert `focusScope.child` type across all three routing tests.
- Verify `Navigator.focusNode.enclosingScope` attaches to 'Navigator Scope' when built via router or navigator.
- Add test verifying `WidgetsApp.router` passes the enclosing `FocusScope` as `child` to `builder`.
- Update `WidgetsApp.builder` docstring to clarify that `child` is enclosed by `FocusScope`.
@mdebbar

mdebbar commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Looks good to me but I'd like for someone from the framework team to take a look as well.

@chunhtai
chunhtai self-requested a review September 11, 2026 16:00

@chunhtai chunhtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change LGTM, but it is the side effect of wrapping focusscope that fixes the semantics tree structure, we should have a test to make sure the semantics tree is correct

Comment thread packages/flutter/test/widgets/app_test.dart
Verify that WidgetsApp.router and WidgetsApp with navigator generate matching semantics tree structures with an enclosing FocusScope node.
- Add semantics tree structure test for WidgetsApp.router with routerConfig.

- Verify route semantics node stability upon dynamic overlay insertion.

- Clarify FocusScope explicitChildNodes documentation comments.
@kevmoo

kevmoo commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

@chunhtai -fixed landed! include more tests

Comment thread packages/flutter/test/widgets/app_test.dart Outdated
…mantics

Replace full-tree TestSemantics.root assertions with targeted tester.semantics.find spot checks on the FocusScope and route semantics nodes.
chunhtai
chunhtai previously approved these changes Sep 16, 2026

@chunhtai chunhtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, minus the comment

Comment thread packages/flutter/test/widgets/app_test.dart Outdated
Comment thread packages/flutter/test/widgets/app_test.dart Outdated
@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 16, 2026
@auto-submit

auto-submit Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/192601, because - The status or check suite ci.yaml validation has failed. Please fix the issues identified (or deflake) before re-applying this label.

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 16, 2026
@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 16, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Sep 17, 2026
Merged via the queue into flutter:master with commit a029daa Sep 17, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 17, 2026
@kevmoo
kevmoo deleted the issue-187561 branch September 18, 2026 06:03
jesswrd pushed a commit to jesswrd/flutter that referenced this pull request Sep 21, 2026
…st (flutter#192995)

In flutter#192601, the `'WidgetsApp.router produces expected semantics tree
structure'` test added in `packages/flutter/test/widgets/app_test.dart`
registered `addTearDown(entry.remove)` for its `OverlayEntry` without
calling `entry.dispose()`.

Because `OverlayEntry` allocates an internal
`ValueNotifier<_OverlayEntryWidgetState?>` that is only disposed in
`OverlayEntry.dispose()`, `Windows
framework_tests_widgets_leak_tracking` failed in `tearDownAll` with 2
undisposed objects (`OverlayEntry` and
`ValueNotifier<_OverlayEntryWidgetState?>`).

This change updates the tear-down callback to call both `entry.remove()`
and `entry.dispose()`, matching
`packages/flutter/test/widgets/overlay_test.dart`.

Fixes flutter#192993

## 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/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Web] Autocomplete loses keyboard focus after options appear when ensureSemantics() + MaterialApp.router are used

3 participants