Skip to content

[go_router] Resolve relative ./ navigation against the imperative top (flutter#182441)#11726

Draft
NadeemIqbal wants to merge 1 commit into
flutter:mainfrom
NadeemIqbal:nadeem/182441-typed-relative-route-parent-resolution
Draft

[go_router] Resolve relative ./ navigation against the imperative top (flutter#182441)#11726
NadeemIqbal wants to merge 1 commit into
flutter:mainfrom
NadeemIqbal:nadeem/182441-typed-relative-route-parent-resolution

Conversation

@NadeemIqbal
Copy link
Copy Markdown

Fixes flutter/flutter#182441.

Description

After router.push('/products'), Flutter's Router calls GoRouteInformationProvider.routerReportsNewRouteInformation with the URI returned by restoreRouteInformation. When GoRouter.optionURLReflectsImperativeAPIs is false (the default), that URI excludes any ImperativeRouteMatches, so the provider's _value.uri is reset from /products back to the non-imperative base /home. The next push('./reviews') then resolves the relative path against that stale _value.uri inside _setValue, sending the user to /home/reviews instead of /products/reviews.

The user-facing symptom of this in go_router_builder (#182441) is that a TypedRelativeGoRoute child shared by multiple parents always resolves to the first parent in the route tree: the generated pushRelative calls context.push('./<child>'), which the provider misresolves.

Verifying the diagnosis

Reproduced at the GoRouter runtime layer (no go_router_builder required):

final router = GoRouter(initialLocation: '/home', routes: <RouteBase>[
  GoRoute(path: '/home',     routes: [GoRoute(path: 'reviews', ...)], ...),
  GoRoute(path: '/products', routes: [GoRoute(path: 'reviews', ...)], ...),
]);
router.push('/products');     // router.state.uri.path == '/products'  ✓
router.push('./reviews');     // expected: /products/reviews
                              // actual on main:  /home/reviews        ✗

Instrumenting _setValue shows _value.uri flipping from /products (set by step 1) back to /home (set by Flutter's routerReportsNewRouteInformation callback) before step 2 runs.

Fix

Resolve ./ paths up-front in push, pushReplacement, and replace using the actual top of their base (drilling through ShellRouteMatch and using the URI of the top ImperativeRouteMatch, falling back to base.uri otherwise). The location passed to _setValue is then already absolute, sidestepping the stale _value.uri.

go is left unchanged: it does not stack and has no base, and the existing relative "./" navigation resolves against current location test in on_enter_test.dart continues to cover the go('./...') path.

Tests

  • Added two regression tests in packages/go_router/test/imperative_api_test.dart mirroring the shared-parent scenario from #182441:
    1. push('./<child>') from an imperative parent.
    2. The same for pushReplacement('./<child>') and replace('./<child>').
  • Full suite: 431/431 pass (was 430/430), no regressions.

Versioning

Bumped go_router 17.2.3 → 17.2.4 and added a CHANGELOG entry per pub versioning philosophy.

List which issues are fixed by this PR.

flutter/flutter#182441

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter (dart format).
  • I signed the CLA.
  • The title of the PR starts with the name of the package surrounded by square brackets ([go_router]).
  • I linked to at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version.
  • I updated CHANGELOG.md to add a description of the change.
  • I added new tests to check the change I am making.
  • I am willing to follow-up on review comments in a timely manner.

After `router.push('/products')`, Flutter's Router calls
`GoRouteInformationProvider.routerReportsNewRouteInformation` with the URI
returned by `restoreRouteInformation`. When
`GoRouter.optionURLReflectsImperativeAPIs` is false (the default), that URI
excludes any `ImperativeRouteMatch`es, so the provider's `_value.uri` is
reset to the non-imperative base (e.g. `/home`). The next
`push('./reviews')` then resolves the relative path against that stale
`_value.uri` inside `_setValue`, sending the user to `/home/reviews`
instead of `/products/reviews`.

The user-facing symptom of this in go_router_builder (#182441) is that a
`TypedRelativeGoRoute` child shared by multiple parents always resolves to
the first parent in the route tree: the generated `pushRelative` calls
`context.push('./<child>')`, which the provider misresolves.

Fix: resolve `./` paths up-front in `push`, `pushReplacement`, and
`replace` using the actual top of their `base` (drilling through
`ShellRouteMatch` and falling back to `base.uri` if no
`ImperativeRouteMatch` is on top). The location passed to `_setValue` is
then already absolute, sidestepping the stale `_value.uri`.

`go` is left unchanged: it does not stack and has no `base`, and the
existing test `relative "./" navigation resolves against current location`
in `on_enter_test.dart` continues to cover that path.

Added two regression tests in `imperative_api_test.dart` mirroring the
shared-parent scenario from #182441 across `push`, `pushReplacement`, and
`replace`. Full suite: 431/431 (was 430/430).

Fixes flutter/flutter#182441.
@github-actions github-actions Bot added p: go_router triage-framework Should be looked at in framework triage labels May 17, 2026
Copy link
Copy Markdown

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

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 updates go_router to version 17.2.4 and addresses an issue where relative navigation using imperative APIs resolved against the incorrect base URI. The implementation introduces _topUriOf and _resolveRelativeAgainstBase helper methods in GoRouteInformationProvider to ensure relative paths resolve against the actual visible top route. The fix is applied to the push, pushReplacement, and replace methods, and is accompanied by new regression tests. I have no feedback to provide.

@stuartmorgan-g
Copy link
Copy Markdown
Collaborator

Thanks for the contribution! I see you've opened three different PRs here, each of which has a different version of the checklist, and none of which uses this repository's actual checklist. Please update all the PRs to use, and follow, the correct checklist, and then mark them as ready for review.

Please pay particular attention to the checklist item about reading and understanding our AI contribution guidelines, as this pattern of variable checklists (along with the opening of a dozen Flutter PRs simultaneously as a new contributor) strongly suggests a heavy reliance on AI in creating the PRs.

@stuartmorgan-g stuartmorgan-g marked this pull request as draft May 18, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p: go_router triage-framework Should be looked at in framework triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

go_router_builder: TypedRelativeRoute always uses root route

2 participants