[go_router] Fix SelectionArea dead zones after ShellRoute navigation#11062
Open
davidmigloz wants to merge 2 commits intoflutter:mainfrom
Open
[go_router] Fix SelectionArea dead zones after ShellRoute navigation#11062davidmigloz wants to merge 2 commits intoflutter:mainfrom
davidmigloz wants to merge 2 commits intoflutter:mainfrom
Conversation
When a SelectionArea wraps a ShellRoute's child (e.g. in a Scaffold body), offstage pages kept their Text widgets registered with the SelectableRegion. These invisible selectables intercepted drag-to-select events, creating dead zones on the active page. For ShellRoute: adds _OffstageSelectionDisabler that detects non-current routes via ModalRoute.of(context) and wraps them with SelectionContainer.disabled. For StatefulShellRoute: wraps inactive branches with SelectionContainer.disabled in _IndexedStackedRouteBranchContainer. Fixes flutter/flutter#182573
There was a problem hiding this comment.
Code Review
The pull request successfully addresses the issue of text selection "dead zones" by disabling selection for offstage routes. However, the current implementation of conditionally wrapping widgets with SelectionContainer.disabled or _OffstageSelectionDisabler will cause the entire subtree (including Navigators and their pages) to be unmounted and remounted whenever a route's visibility changes. This leads to a complete loss of state (e.g., scroll positions, text field contents) for those routes. I have suggested an alternative approach that toggles the registrar property of a persistent SelectionContainer to maintain the widget tree structure and preserve state.
chunhtai
reviewed
Feb 18, 2026
- Convert _OffstageSelectionDisabler to StatefulWidget with GlobalKey-based reparenting to preserve descendant State across tree structure changes - Add _SelectionGuard StatefulWidget in route.dart for StatefulShellRoute branch containers with the same GlobalKey preservation pattern - Fix type parameter mismatch: try both <dynamic> and <void> in _addSelectionGuardToPage so user pageBuilder pages (which default to <dynamic>) get the selection guard applied - Switch to pending_changelogs/ instead of direct CHANGELOG.md/pubspec.yaml edits (revert version back to 17.1.0) - Fix omit_obvious_local_variable_types lint in 3 test declarations - Add 4 new tests: state preservation for ShellRoute and StatefulShellRoute, and <dynamic> type parameter coverage for NoTransitionPage and CustomTransitionPage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When a
SelectionAreawraps aShellRoute's child (a common pattern for enabling text selection across an app), offstage pages kept theirTextwidgets registered with theSelectableRegion. These invisible selectables intercepted drag-to-select events, creating "dead zones" on the currently visible page where selection silently failed.Fix
ShellRoute: Adds_OffstageSelectionDisabler— a lightweight widget inserted around each route's content that detects non-current routes viaModalRoute.of(context)and wraps them withSelectionContainer.disabledto unregister their selectables.StatefulShellRoute: Wraps inactive branches withSelectionContainer.disabledin_IndexedStackedRouteBranchContainer._buildRouteBranchContainer. Navigator state is preserved viaGlobalObjectKeyreparenting.Reproduction
Fixes flutter/flutter#182573
Tests
Added 12 new widget tests in
builder_test.dartcovering: