Repository navigation
[go_router] Fix shell route key collisions and dynamic branch updates - #12549
auto-submit[bot] merged 18 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request resolves ShellRoute key collisions during imperative navigation by introducing custom page and navigator key classes (_ShellRoutePageKey and _ShellRouteNavigatorKey) and updating the match cloning logic to scope keys when collisions are detected. Feedback from the review suggests replacing GlobalObjectKey with ValueKey to prevent state loss during rebuilds, and implementing a recursive collision check to correctly handle nested ShellRouteMatches.
|
Hi! I've been working on a related fix and wanted to discuss it so we can land on a common implementation that fixes everything right the first time. My case is a little different from yours, I have an app that rebuild their route tree from application state, via You can look at what I did here : https://github.com/MaximeRougieux/flutter-packages/commits/fix/go-router-dynamic-shell-routes-handling/. Both our fixes does not cover the issue the other is trying to fix, but we are touching the same logic in match.dart. As my AI chatbot tells it : we're fixing two different problems on the same line, in opposite directions: you're making the key stricter about object identity, I'm making it not depend on object identity at all. This being said, your choice of using a custom ValueKey class which overrides == is better than what I did, but for it to fix my issue, we'd need it to compare a stable identity. What I chose to look at is:
It would not impact your fix, which we can verify with the tests you added. I already sent you my fork, if you want, I can directly implement the changes and push them on this PR, or you can have a look, whichever is the simplest for you. If you and/or the maintainers prefer, I can wait for this to land before pushing a separate PR. Some related issues: |
Use stable configured keys for shell page identity, retain stateful branch state by navigator key, and reparse imperative matches nested under shell routes. Co-authored-by: Maxime Rougieux <[email protected]>
|
Thank you @Gorniv! Everything works as expected on my end 😄 |
|
Thanks for confirming, @MaximeRougieux! I added follow-up regression coverage in
The full |
elliette
left a comment
There was a problem hiding this comment.
Thank you! I have one suggestion, but otherwise looks good to me.
|
@elliette FYI, I'm waiting on this PR to be merged because it contains multiple fixes I need, if @Gorniv's doesn't come back, I'll open up another one integrating the requested change to his changes. Could you tell me what's the typical acceptable delay before closing the PR due to inactivity please? |
Piinks
left a comment
There was a problem hiding this comment.
Thanks for working on this!
Can you add regression tests covering the specific reproduction flows cited:
- flutter/flutter#148712
- Rebuilding
GoRouterwithShellRoute(builder: ..., navigatorKey: ...)without duplicate GlobalKey crashes.
- Rebuilding
- flutter/flutter#140586
- Navigating
A -> Push B -> Push C -> PushReplacement A -> Push Bwithout!keyReservation.contains(key)failures.
- Navigating
|
Thanks for the review! I added exact regression coverage for both reported flows in 64ec450: |
elliette
left a comment
There was a problem hiding this comment.
Thank you for the updates! This all LGTM.
Piinks
left a comment
There was a problem hiding this comment.
I think there are a few more things to address, can you take a look at and incorporate these tests cases to make sure they work as expected?
Widen cloned match lists, reset invalid branch bases, and scope saved imperative matches to the shell. Identify reparsed branches by navigator key and share observer merging across active, inactive, and preloaded navigators. Add regression coverage for removed routes, nested replacement, observer opt-out, and clipping.
Piinks
left a comment
There was a problem hiding this comment.
LGTM thank you for contributing!
Roll Packages from a1ed23c to da40eaf (33 revisions) flutter/packages@a1ed23c...da40eaf 2026-10-15 [email protected] [pigeon]add support for sealed classes in native interop (flutter/packages#13175) 2026-10-15 [email protected] [pigeon] Replace full JNI bindings with stubs for API classes that aren't needed (flutter/packages#13173) 2026-10-14 [email protected] [material_ui] Fix self-comparison and assert bugs in semantics_tester (flutter/packages#12722) 2026-10-14 [email protected] [camera_avfoundation] Replace the deprecated 'videoOrientation' with 'videoRotationAngle' on iOS 17+ (flutter/packages#13247) 2026-10-14 [email protected] [material_ui] Fix GridTile crash when the incoming constraints are unbounded (flutter/packages#12586) 2026-10-14 [email protected] [in_app_purchase_storekit] Remove unused and deprecated "SKDownload" hosted content (flutter/packages#13222) 2026-10-14 [email protected] [material_ui] Add supportingTextPadding to InputDecorator (flutter/packages#12769) 2026-10-14 [email protected] [vector_graphics_codec] Add support for encoding and decoding paint blur commands (flutter/packages#13148) 2026-10-14 [email protected] [go_router] Fix redirect zone async error handling (flutter/packages#12836) 2026-10-14 [email protected] Sync release-go_router-18.1.0 to main (flutter/packages#13260) 2026-10-14 [email protected] Sync release-cupertino_ui-1.1.3 to main (flutter/packages#13258) 2026-10-14 [email protected] [material_ui] add enableInlinePrediction parameter to TextFormField (flutter/packages#12793) 2026-10-14 [email protected] Sync release-material_ui-1.7.0 to main (flutter/packages#13259) 2026-10-14 [email protected] [material_ui] Add the Material shapes. (flutter/packages#12771) 2026-10-14 [email protected] [material_ui] Add tertiary tap callbacks to InkResponse and InkWell (flutter/packages#12676) 2026-10-14 [email protected] Roll Flutter from 4249ccb to 426be96 (44 revisions) (flutter/packages#13256) 2026-10-14 [email protected] [material_ui] fixes expanded state in the a11y_assessment home page dropdown (flutter/packages#13219) 2026-10-14 [email protected] [VPAT] Fix semantics in a11y_assessments drawer, radio, and rail demos (flutter/packages#13159) 2026-10-14 [email protected] [google_maps_flutter] Add onPointOfInterestTap (flutter/packages#11872) 2026-10-14 [email protected] [go_router] Fix shell route key collisions and dynamic branch updates (flutter/packages#12549) 2026-10-14 [email protected] Manual roll Flutter from d03768e to 4249ccb (158 revisions) (flutter/packages#13220) 2026-10-14 [email protected] [image_picker_ios] Require Flutter 3.47 / iOS 15 and drop deprecated kUTType code paths (flutter/packages#13224) 2026-10-13 [email protected] [in_app_purchase_storekit] Fixes Storekit 2 flaky tests (flutter/packages#13213) 2026-10-13 [email protected] [local_auth] Convert Android to new Pigeon suspend support (flutter/packages#12894) 2026-10-13 [email protected] [material_ui] Add enabled parameter to DropdownButtonFormField (flutter/packages#12610) 2026-10-13 [email protected] [go_router] Link ShellRoute docs to StatefulShellRoute (flutter/packages#13120) 2026-10-13 [email protected] FadeForwards page transition performance (flutter/packages#13161) 2026-10-13 [email protected] [in_app_purchase_android] Guard async billing callbacks against duplicate listener responses (flutter/packages#12640) 2026-10-13 [email protected] [google_sign_in] Remove the Objective-C exception catcher (flutter/packages#13156) 2026-10-13 [email protected] [camera_avfoundation] Remove availability checks below the package minimum iOS 13 (flutter/packages#13214) 2026-10-13 [email protected] [google_maps_flutter_ios] Remove iOS 14 availability check from example UI tests (flutter/packages#13218) 2026-10-13 [email protected] [google_sign_in] Convert implementation to Kotlin (flutter/packages#13005) 2026-10-13 [email protected] [image_picker_ios] PR 2/5 Add a PHImageManager image-data seam and migrate plugin native tests to Swift Testing (flutter/packages#13079) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: ...
Roll Packages from a1ed23c to da40eaf (33 revisions) flutter/packages@a1ed23c...da40eaf 2026-10-15 [email protected] [pigeon]add support for sealed classes in native interop (flutter/packages#13175) 2026-10-15 [email protected] [pigeon] Replace full JNI bindings with stubs for API classes that aren't needed (flutter/packages#13173) 2026-10-14 [email protected] [material_ui] Fix self-comparison and assert bugs in semantics_tester (flutter/packages#12722) 2026-10-14 [email protected] [camera_avfoundation] Replace the deprecated 'videoOrientation' with 'videoRotationAngle' on iOS 17+ (flutter/packages#13247) 2026-10-14 [email protected] [material_ui] Fix GridTile crash when the incoming constraints are unbounded (flutter/packages#12586) 2026-10-14 [email protected] [in_app_purchase_storekit] Remove unused and deprecated "SKDownload" hosted content (flutter/packages#13222) 2026-10-14 [email protected] [material_ui] Add supportingTextPadding to InputDecorator (flutter/packages#12769) 2026-10-14 [email protected] [vector_graphics_codec] Add support for encoding and decoding paint blur commands (flutter/packages#13148) 2026-10-14 [email protected] [go_router] Fix redirect zone async error handling (flutter/packages#12836) 2026-10-14 [email protected] Sync release-go_router-18.1.0 to main (flutter/packages#13260) 2026-10-14 [email protected] Sync release-cupertino_ui-1.1.3 to main (flutter/packages#13258) 2026-10-14 [email protected] [material_ui] add enableInlinePrediction parameter to TextFormField (flutter/packages#12793) 2026-10-14 [email protected] Sync release-material_ui-1.7.0 to main (flutter/packages#13259) 2026-10-14 [email protected] [material_ui] Add the Material shapes. (flutter/packages#12771) 2026-10-14 [email protected] [material_ui] Add tertiary tap callbacks to InkResponse and InkWell (flutter/packages#12676) 2026-10-14 [email protected] Roll Flutter from 4249ccb to 426be96 (44 revisions) (flutter/packages#13256) 2026-10-14 [email protected] [material_ui] fixes expanded state in the a11y_assessment home page dropdown (flutter/packages#13219) 2026-10-14 [email protected] [VPAT] Fix semantics in a11y_assessments drawer, radio, and rail demos (flutter/packages#13159) 2026-10-14 [email protected] [google_maps_flutter] Add onPointOfInterestTap (flutter/packages#11872) 2026-10-14 [email protected] [go_router] Fix shell route key collisions and dynamic branch updates (flutter/packages#12549) 2026-10-14 [email protected] Manual roll Flutter from d03768e to 4249ccb (158 revisions) (flutter/packages#13220) 2026-10-14 [email protected] [image_picker_ios] Require Flutter 3.47 / iOS 15 and drop deprecated kUTType code paths (flutter/packages#13224) 2026-10-13 [email protected] [in_app_purchase_storekit] Fixes Storekit 2 flaky tests (flutter/packages#13213) 2026-10-13 [email protected] [local_auth] Convert Android to new Pigeon suspend support (flutter/packages#12894) 2026-10-13 [email protected] [material_ui] Add enabled parameter to DropdownButtonFormField (flutter/packages#12610) 2026-10-13 [email protected] [go_router] Link ShellRoute docs to StatefulShellRoute (flutter/packages#13120) 2026-10-13 [email protected] FadeForwards page transition performance (flutter/packages#13161) 2026-10-13 [email protected] [in_app_purchase_android] Guard async billing callbacks against duplicate listener responses (flutter/packages#12640) 2026-10-13 [email protected] [google_sign_in] Remove the Objective-C exception catcher (flutter/packages#13156) 2026-10-13 [email protected] [camera_avfoundation] Remove availability checks below the package minimum iOS 13 (flutter/packages#13214) 2026-10-13 [email protected] [google_maps_flutter_ios] Remove iOS 14 availability check from example UI tests (flutter/packages#13218) 2026-10-13 [email protected] [google_sign_in] Convert implementation to Kotlin (flutter/packages#13005) 2026-10-13 [email protected] [image_picker_ios] PR 2/5 Add a PHImageManager image-data seam and migrate plugin native tests to Swift Testing (flutter/packages#13079) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: ...
This supersedes #11143, which was closed as inactive. It incorporates the review feedback from that PR.
Problem
When
push()navigates between sibling or nested regularShellRoutebranches, multiple shell pages and navigators can coexist in the widget tree.This could produce duplicate page or widget keys because:
_CustomNavigatorwas keyed usingGlobalObjectKey(navigatorKey.hashCode), so distinct navigator keys with the same hash were treated as the same key.ShellRouteMatch.pageKeywas derived only fromroute.hashCode.ShellRoutethat was already present in the imperative stack reused its page and navigator identities.The first implementation in #11143 created fresh page and navigator keys for every
ShellRouteMatch. Review correctly pointed out that this made navigator identity unstable across route reparses and prevented the configuredShellRoute.navigatorKeyfrom consistently identifying its navigator.RoutingConfigupdates could also leave inactive loadedStatefulShellRoutebranches holding matches and navigator widgets built from the old route objects.Fix
This version addresses that feedback by:
hashCodevalues collide;_CustomNavigatorwrapper with a value-equalValueKeyof its navigator key;ShellRoutematch;ImperativeRouteMatch.pageKey;StatefulShellRoutebranches and rebuilding their cached navigator widgets when route objects change;pushReplacement();notifyRootObserver;Tests
The regression test
push to a sibling shell route under the same parent shell routeforces both navigator-key and route-hash collisions.It covers:
refresh();ShellRouteMatchtrees;go()after the imperative stack is removed.Additional regressions cover the nested push sequence in the review comment, inactive loaded branches after a
RoutingConfigupdate (review comment), and the exact flows from flutter/flutter#148712 and flutter/flutter#140586.Eight additional regression cases cover the follow-up review findings: a removed base route with a surviving push (with and without preload), nested
pushReplacement(), a removed pushed route, and branch/root observer behavior for both preload andnotifyRootObserversettings. The observer regression uses pagelessNavigator.push()so a GoRouter rebuild cannot mask detached observers; it also verifies navigator-state preservation and updatedclipBehavior.No screenshots are included because this is a routing/runtime fix.
Validation
flutter test --no-pub: 471 passedflutter analyze --no-pub: no issuesflutter_plugin_tools validate --check-for-missing-changes --packages go_router: no issuesflutter_plugin_tools publish-check --packages go_router: no issuesRelease note: This package uses batch releases, so the fix is recorded in
pending_changelogs/shell_route_key_collisions.yaml;pubspec.yamlandCHANGELOG.mdremain unchanged.Pre-Review Checklist
[shared_preferences]///).Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2