Skip to content

[go_router] Added proper redirect handling for ShellRoute.$route and StatefulShellRoute.$route for proper redirection handling in case of code generation #6841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 14.2.0

- Added proper `redirect` handling for `ShellRoute.$route` and `StatefulShellRoute.$route` for proper redirection handling in case of code generation.

## 14.1.4

- Fixes a URL in `navigation.md`.
Expand Down
25 changes: 25 additions & 0 deletions packages/go_router/lib/src/route_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ abstract class ShellRouteData extends RouteData {
'One of `builder` or `pageBuilder` must be implemented.',
);

/// An optional redirect function for this route.
///
/// Subclasses must override one of [build], [buildPage], or
/// [redirect].
///
/// Corresponds to [GoRoute.redirect].
FutureOr<String?> redirect(BuildContext context, GoRouterState state) => null;

/// A helper function used by generated code.
///
/// Should not be used directly.
Expand All @@ -174,6 +182,9 @@ abstract class ShellRouteData extends RouteData {
return (_stateObjectExpando[state] ??= factory(state)) as T;
}

FutureOr<String?> redirect(BuildContext context, GoRouterState state) =>
factoryImpl(state).redirect(context, state);

Widget builder(
BuildContext context,
GoRouterState state,
Expand Down Expand Up @@ -204,6 +215,7 @@ abstract class ShellRouteData extends RouteData {
navigatorKey: navigatorKey,
observers: observers,
restorationScopeId: restorationScopeId,
redirect: redirect,
);
}

Expand All @@ -221,6 +233,14 @@ abstract class StatefulShellRouteData extends RouteData {
/// Default const constructor
const StatefulShellRouteData();

/// An optional redirect function for this route.
///
/// Subclasses must override one of [build], [buildPage], or
/// [redirect].
///
/// Corresponds to [GoRoute.redirect].
FutureOr<String?> redirect(BuildContext context, GoRouterState state) => null;

/// [pageBuilder] is used to build the page
Page<void> pageBuilder(
BuildContext context,
Expand Down Expand Up @@ -275,6 +295,9 @@ abstract class StatefulShellRouteData extends RouteData {
navigationShell,
);

FutureOr<String?> redirect(BuildContext context, GoRouterState state) =>
factoryImpl(state).redirect(context, state);

if (navigatorContainerBuilder != null) {
return StatefulShellRoute(
branches: branches,
Expand All @@ -283,6 +306,7 @@ abstract class StatefulShellRouteData extends RouteData {
navigatorContainerBuilder: navigatorContainerBuilder,
parentNavigatorKey: parentNavigatorKey,
restorationScopeId: restorationScopeId,
redirect: redirect,
);
}
return StatefulShellRoute.indexedStack(
Expand All @@ -291,6 +315,7 @@ abstract class StatefulShellRouteData extends RouteData {
pageBuilder: pageBuilder,
parentNavigatorKey: parentNavigatorKey,
restorationScopeId: restorationScopeId,
redirect: redirect,
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: go_router
description: A declarative router for Flutter based on Navigation 2 supporting
deep linking, data-driven routes and more
version: 14.1.4
version: 14.2.0
repository: https://github.com/flutter/packages/tree/main/packages/go_router
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22

Expand Down
83 changes: 83 additions & 0 deletions packages/go_router/test/route_data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,20 @@ import 'package:go_router/go_router.dart';

class _GoRouteDataBuild extends GoRouteData {
const _GoRouteDataBuild();

@override
Widget build(BuildContext context, GoRouterState state) =>
const SizedBox(key: Key('build'));
}

class _ShellRouteDataRedirectPage extends ShellRouteData {
const _ShellRouteDataRedirectPage();

@override
FutureOr<String> redirect(BuildContext context, GoRouterState state) =>
'/build-page';
}

class _ShellRouteDataBuilder extends ShellRouteData {
const _ShellRouteDataBuilder();

Expand Down Expand Up @@ -49,7 +58,9 @@ class _ShellRouteDataWithKey extends ShellRouteData {

class _GoRouteDataBuildWithKey extends GoRouteData {
const _GoRouteDataBuildWithKey(this.key);

final Key key;

@override
Widget build(BuildContext context, GoRouterState state) => SizedBox(key: key);
}
Expand All @@ -71,6 +82,7 @@ final ShellRoute _shellRouteDataBuilder = ShellRouteData.$route(

class _GoRouteDataBuildPage extends GoRouteData {
const _GoRouteDataBuildPage();

@override
Page<void> buildPage(BuildContext context, GoRouterState state) =>
const MaterialPage<void>(
Expand All @@ -95,6 +107,14 @@ class _ShellRouteDataPageBuilder extends ShellRouteData {
);
}

class _StatefulShellRouteDataRedirectPage extends StatefulShellRouteData {
const _StatefulShellRouteDataRedirectPage();

@override
FutureOr<String> redirect(BuildContext context, GoRouterState state) =>
'/build-page';
}

final GoRoute _goRouteDataBuildPage = GoRouteData.$route(
path: '/build-page',
factory: (GoRouterState state) => const _GoRouteDataBuildPage(),
Expand All @@ -110,6 +130,21 @@ final ShellRoute _shellRouteDataPageBuilder = ShellRouteData.$route(
],
);

final ShellRoute _shellRouteDataRedirect = ShellRouteData.$route(
factory: (GoRouterState state) => const _ShellRouteDataPageBuilder(),
routes: <RouteBase>[
ShellRouteData.$route(
factory: (GoRouterState state) => const _ShellRouteDataRedirectPage(),
routes: <RouteBase>[
GoRouteData.$route(
path: '/child',
factory: (GoRouterState state) => const _GoRouteDataBuild(),
),
],
),
],
);

class _StatefulShellRouteDataBuilder extends StatefulShellRouteData {
const _StatefulShellRouteDataBuilder();

Expand Down Expand Up @@ -174,6 +209,7 @@ final StatefulShellRoute _statefulShellRouteDataPageBuilder =

class _GoRouteDataRedirectPage extends GoRouteData {
const _GoRouteDataRedirectPage();

@override
FutureOr<String> redirect(BuildContext context, GoRouterState state) =>
'/build-page';
Expand Down Expand Up @@ -311,6 +347,23 @@ void main() {
expect(find.byKey(const Key('page-builder')), findsOneWidget);
},
);

testWidgets(
'It should redirect using the overridden redirect method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/child',
routes: <RouteBase>[
_goRouteDataBuildPage,
_shellRouteDataRedirect,
],
);
addTearDown(goRouter.dispose);
await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsOneWidget);
},
);
});

group('StatefulShellRouteData', () {
Expand Down Expand Up @@ -381,6 +434,36 @@ void main() {
},
);

testWidgets(
'It should redirect using the overridden StatefulShellRoute redirect method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/child',
routes: <RouteBase>[
_goRouteDataBuildPage,
StatefulShellRouteData.$route(
factory: (GoRouterState state) =>
const _StatefulShellRouteDataRedirectPage(),
branches: <StatefulShellBranch>[
StatefulShellBranchData.$branch(
routes: <GoRoute>[
GoRouteData.$route(
path: '/child',
factory: (GoRouterState state) => const _GoRouteDataBuild(),
),
],
)
],
)
],
);
addTearDown(goRouter.dispose);
await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsOneWidget);
},
);

testWidgets(
'It should redirect using the overridden redirect method',
(WidgetTester tester) async {
Expand Down