[go_router_builder] Conditional redirects fail for nested GoRouteData routes with multiple ShellRoute levels
- Dominant language
- Dart
- Stars
- 179k
- Forks
- 31.1k
- PR merge metrics
- PR metrics pending
Description
## Description
Using `go_router_builder` with a `GoRouteData` class that only overrides `redirect` causes an `UnimplementedError` when the redirect is conditional and returns `null` for descendant routes.
This works as expected with a manually defined `GoRoute`, but fails with generated typed routes.
The problem becomes more visible when the application contains nested `StatefulShellRoute`s and multiple levels of parent redirects.
## Environment
- `go_router: 17.5.0`
- `go_router_builder: 4.4.0`
- Flutter application running on mobile
- Nested `StatefulShellRoute` navigation
## Minimal scenario
Consider a parent route that redirects only when its exact root path is visited:
```dart
class MainRoute extends GoRouteData {
@override
String? redirect(BuildContext context, GoRouterState state) {
if (state.uri.path == '/main') {
return '/main/home';
}
// The current location is already a descendant route.
return null;
}
}
```
The intended behavior is:
```text
/main
-> redirect to /main/home
/main/home
-> redirect returns null
-> render the child route
```
Returning `null` for `/main/home` is intentional. It means that the current descendant route should be allowed to render.
The same pattern occurs with two nested shells:
```text
/main
-> /main/manage
-> /main/manage/analysis
```
For `/main/manage/analysis`, the expected redirect flow is:
```text
Main route redirect:
descendant path -> null
Manage route redirect:
descendant path -> null
Inner shell:
render the analysis branch
```
## Actual behavior
When the typed parent route returns `null`, the generated route still has `builder` and `pageBuilder` callbacks.
The route then falls through to the default `GoRouteData.build()` implementation and throws:
```text
UnimplementedError:
One of `build` or `buildPage` must be implemented.
```
Relevant stack frames:
```text
_GoRouteDataBase.build
_createGoRouteParameters.
_CustomNavigatorState._buildPageForGoRoute
```
This means that a conditional redirect-only parent route is treated as a page-building route after returning `null.
## Expected behavior
A typed route that only overrides `redirect` should be able to return `null` for descendant paths without attempting to build a page.
In particular, nested route hierarchies should support:
1. A parent route redirecting its exact root path to a default child.
2. The same parent route returning `null` for already-matched descendants.
3. Multiple nested parent redirects across multiple `StatefulShellRoute` levels.
4. Child routes and shell branches being rendered after all applicable parent redirects return `null`.
This should behave consistently with a manually defined redirect-only `GoRoute`.
## Possible implementation directions
One possible solution would be for generated typed routes to omit `builder` and `pageBuilder` when the `GoRouteData` class does not override either `build` or `buildPage`.
Another option would be for `go_router` to recognize redirect-only typed routes and exclude them from page construction when the redirect returns `null`, while still allowing their child routes to render.
The important behavior is that returning `null` from a parent redirect should mean “allow the current descendant route”, rather than invoking the default `GoRouteData.build()` implementation.
## Related issue
This appears related to the older issue:
https://github.com/flutter/flutter/issues/103368
That issue reproduces a similar `UnimplementedError` after redirecting from a typed parent route to a child route. It is closed, but the nested conditional redirect behavior described here is still reproducible with current versions.
Contributor guide
Research direction
Start by reproducing the minimal conditional redirect scenario with nested StatefulShellRoutes, then trace the mentioned _GoRouteDataBase.build, _createGoRouteParameters, and _CustomNavigatorState._buildPageForGoRoute stack frames. Check how generated redirect-only routes expose builder and pageBuilder callbacks. Done means descendant routes render without UnimplementedError when parent redirects return null, including across multiple shell levels.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100