From e3bcbb515a9517f4806cad4f7b7479353222c64e Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Thu, 28 Mar 2024 18:36:49 -0300 Subject: [PATCH 1/8] [go_router] Feat add route redirect shellroutes (#114559) --- packages/go_router/lib/src/configuration.dart | 11 +- packages/go_router/lib/src/route.dart | 127 +++++++++--------- 2 files changed, 72 insertions(+), 66 deletions(-) diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index 09764747aae..ddd4961289f 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -394,14 +394,13 @@ class RouteConfiguration { return prevMatchList; } - final List routeMatches = []; + final List routeMatches = []; prevMatchList.visitRouteMatches((RouteMatchBase match) { - if (match is RouteMatch) { + if (match.route.redirect != null) { routeMatches.add(match); } return true; }); - final FutureOr routeLevelRedirectResult = _getRouteLevelRedirect(context, prevMatchList, routeMatches, 0); @@ -434,18 +433,18 @@ class RouteConfiguration { FutureOr _getRouteLevelRedirect( BuildContext context, RouteMatchList matchList, - List routeMatches, + List routeMatches, int currentCheckIndex, ) { if (currentCheckIndex >= routeMatches.length) { return null; } - final RouteMatch match = routeMatches[currentCheckIndex]; + final RouteMatchBase match = routeMatches[currentCheckIndex]; FutureOr processRouteRedirect(String? newLocation) => newLocation ?? _getRouteLevelRedirect( context, matchList, routeMatches, currentCheckIndex + 1); - final GoRoute route = match.route; + final RouteBase route = match.route; FutureOr routeRedirectResult; if (route.redirect != null) { routeRedirectResult = route.redirect!( diff --git a/packages/go_router/lib/src/route.dart b/packages/go_router/lib/src/route.dart index 2fa4bd04640..bc02b5bdf1a 100644 --- a/packages/go_router/lib/src/route.dart +++ b/packages/go_router/lib/src/route.dart @@ -151,10 +151,67 @@ typedef ExitCallback = FutureOr Function(BuildContext context); @immutable abstract class RouteBase with Diagnosticable { const RouteBase._({ + this.redirect, required this.routes, required this.parentNavigatorKey, }); + /// An optional redirect function for this route. + /// + /// In the case that you like to make a redirection decision for a specific + /// route (or sub-route), consider doing so by passing a redirect function to + /// the GoRoute constructor. + /// + /// For example: + /// ``` + /// final GoRouter _router = GoRouter( + /// routes: [ + /// GoRoute( + /// path: '/', + /// redirect: (_) => '/family/${Families.data[0].id}', + /// ), + /// GoRoute( + /// path: '/family/:fid', + /// pageBuilder: (BuildContext context, GoRouterState state) => ..., + /// ), + /// ], + /// ); + /// ``` + /// + /// If there are multiple redirects in the matched routes, the parent route's + /// redirect takes priority over sub-route's. + /// + /// For example: + /// ``` + /// final GoRouter _router = GoRouter( + /// routes: [ + /// GoRoute( + /// path: '/', + /// redirect: (_) => '/page1', // this takes priority over the sub-route. + /// routes: [ + /// GoRoute( + /// path: 'child', + /// redirect: (_) => '/page2', + /// ), + /// ], + /// ), + /// ], + /// ); + /// ``` + /// + /// The `context.go('/child')` will be redirected to `/page1` instead of + /// `/page2`. + /// + /// Redirect can also be used for conditionally preventing users from visiting + /// routes, also known as route guards. One canonical example is user + /// authentication. See [Redirection](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/redirection.dart) + /// for a complete runnable example. + /// + /// If [BuildContext.dependOnInheritedWidgetOfExactType] is used during the + /// redirection (which is how `of` method is usually implemented), a + /// re-evaluation will be triggered if the [InheritedWidget] changes. + final GoRouterRedirect? redirect; + /// The list of child routes associated with this route. final List routes; @@ -208,7 +265,7 @@ class GoRoute extends RouteBase { this.builder, this.pageBuilder, super.parentNavigatorKey, - this.redirect, + super.redirect, this.onExit, super.routes = const [], }) : assert(path.isNotEmpty, 'GoRoute path cannot be empty'), @@ -324,62 +381,6 @@ class GoRoute extends RouteBase { /// final GoRouterWidgetBuilder? builder; - /// An optional redirect function for this route. - /// - /// In the case that you like to make a redirection decision for a specific - /// route (or sub-route), consider doing so by passing a redirect function to - /// the GoRoute constructor. - /// - /// For example: - /// ``` - /// final GoRouter _router = GoRouter( - /// routes: [ - /// GoRoute( - /// path: '/', - /// redirect: (_) => '/family/${Families.data[0].id}', - /// ), - /// GoRoute( - /// path: '/family/:fid', - /// pageBuilder: (BuildContext context, GoRouterState state) => ..., - /// ), - /// ], - /// ); - /// ``` - /// - /// If there are multiple redirects in the matched routes, the parent route's - /// redirect takes priority over sub-route's. - /// - /// For example: - /// ``` - /// final GoRouter _router = GoRouter( - /// routes: [ - /// GoRoute( - /// path: '/', - /// redirect: (_) => '/page1', // this takes priority over the sub-route. - /// routes: [ - /// GoRoute( - /// path: 'child', - /// redirect: (_) => '/page2', - /// ), - /// ], - /// ), - /// ], - /// ); - /// ``` - /// - /// The `context.go('/child')` will be redirected to `/page1` instead of - /// `/page2`. - /// - /// Redirect can also be used for conditionally preventing users from visiting - /// routes, also known as route guards. One canonical example is user - /// authentication. See [Redirection](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/redirection.dart) - /// for a complete runnable example. - /// - /// If [BuildContext.dependOnInheritedWidgetOfExactType] is used during the - /// redirection (which is how `of` method is usually implemented), a - /// re-evaluation will be triggered if the [InheritedWidget] changes. - final GoRouterRedirect? redirect; - /// Called when this route is removed from GoRouter's route history. /// /// Some example this callback may be called: @@ -457,9 +458,11 @@ class GoRoute extends RouteBase { /// as [ShellRoute] and [StatefulShellRoute]. abstract class ShellRouteBase extends RouteBase { /// Constructs a [ShellRouteBase]. - const ShellRouteBase._( - {required super.routes, required super.parentNavigatorKey}) - : super._(); + const ShellRouteBase._({ + super.redirect, + required super.routes, + required super.parentNavigatorKey, + }) : super._(); static void _debugCheckSubRouteParentNavigatorKeys( List subRoutes, GlobalKey navigatorKey) { @@ -622,6 +625,7 @@ class ShellRouteContext { class ShellRoute extends ShellRouteBase { /// Constructs a [ShellRoute]. ShellRoute({ + super.redirect, this.builder, this.pageBuilder, this.observers, @@ -782,6 +786,7 @@ class StatefulShellRoute extends ShellRouteBase { /// [navigatorContainerBuilder]. StatefulShellRoute({ required this.branches, + super.redirect, this.builder, this.pageBuilder, required this.navigatorContainerBuilder, @@ -808,12 +813,14 @@ class StatefulShellRoute extends ShellRouteBase { /// for a complete runnable example using StatefulShellRoute.indexedStack. StatefulShellRoute.indexedStack({ required List branches, + GoRouterRedirect? redirect, StatefulShellRouteBuilder? builder, GlobalKey? parentNavigatorKey, StatefulShellRoutePageBuilder? pageBuilder, String? restorationScopeId, }) : this( branches: branches, + redirect: redirect, builder: builder, pageBuilder: pageBuilder, parentNavigatorKey: parentNavigatorKey, From 9cfe1b256b4e569f4157e0fa3902c228724ed0f0 Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Tue, 9 Apr 2024 11:06:26 -0300 Subject: [PATCH 2/8] chore: Bump version --- packages/go_router/CHANGELOG.md | 4 ++++ packages/go_router/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 774d71c756e..cc4eef4b87e 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,3 +1,7 @@ +## 13.3.0 + +- Feat add route redirect to ShellRoutes + ## 13.2.3 - Fixes an issue where deep links without path caused an exception diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index 1fc99858822..489e564b6b5 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -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: 13.2.3 +version: 13.3.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 From f1a7165bdc1d620208d883b3fc69db779ff3443e Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Tue, 9 Apr 2024 16:18:40 -0300 Subject: [PATCH 3/8] fix: RouteRedirectResult null check --- packages/go_router/lib/src/configuration.dart | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index ddd4961289f..2302523c8f9 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -445,13 +445,10 @@ class RouteConfiguration { _getRouteLevelRedirect( context, matchList, routeMatches, currentCheckIndex + 1); final RouteBase route = match.route; - FutureOr routeRedirectResult; - if (route.redirect != null) { - routeRedirectResult = route.redirect!( - context, - match.buildState(this, matchList), - ); - } + FutureOr routeRedirectResult = route.redirect?.call( + context, + match.buildState(this, matchList), + ); if (routeRedirectResult is String?) { return processRouteRedirect(routeRedirectResult); } From 901c6148d0e381d7c4213e052e7234b0782f39bb Mon Sep 17 00:00:00 2001 From: Victor Ohashi <38299943+VictorOhashi@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:18:21 -0300 Subject: [PATCH 4/8] Update packages/go_router/CHANGELOG.md Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com> --- packages/go_router/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index cc4eef4b87e..55f3aa3dcb2 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,6 +1,6 @@ ## 13.3.0 -- Feat add route redirect to ShellRoutes +- Adds route redirect to ShellRoutes ## 13.2.3 From e4a700ad8d9e218418880465c7afa1c10993b349 Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Sun, 21 Apr 2024 10:50:23 -0300 Subject: [PATCH 5/8] fix: Route configuration --- packages/go_router/lib/src/configuration.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index 2302523c8f9..bef882e1460 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -445,7 +445,7 @@ class RouteConfiguration { _getRouteLevelRedirect( context, matchList, routeMatches, currentCheckIndex + 1); final RouteBase route = match.route; - FutureOr routeRedirectResult = route.redirect?.call( + final FutureOr routeRedirectResult = route.redirect!.call( context, match.buildState(this, matchList), ); From c6f2e6b8a42916110819ad78b66f464535707114 Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Sun, 21 Apr 2024 10:50:38 -0300 Subject: [PATCH 6/8] test: Add shell route redirect testes --- packages/go_router/test/go_router_test.dart | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 57d8612c076..db0a913277a 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2462,6 +2462,94 @@ void main() { expect( router.routerDelegate.currentConfiguration.uri.toString(), '/other'); }); + + testWidgets('redirect when go to a shell route', + (WidgetTester tester) async { + final List routes = [ + ShellRoute( + redirect: (BuildContext context, GoRouterState state) => '/dummy', + builder: (BuildContext context, GoRouterState state, Widget child) => + Scaffold(appBar: AppBar(), body: child), + routes: [ + GoRoute( + path: '/other', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + GoRoute( + path: '/other2', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + ], + ), + GoRoute( + path: '/dummy', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + ]; + + final GoRouter router = await createRouter(routes, tester); + + for (final String shellRoute in ['/other', '/other2']) { + router.go(shellRoute); + await tester.pump(); + expect( + router.routerDelegate.currentConfiguration.uri.toString(), + '/dummy', + ); + } + }); + + testWidgets('redirect when go to a stateful shell route', + (WidgetTester tester) async { + final List routes = [ + StatefulShellRoute.indexedStack( + redirect: (BuildContext context, GoRouterState state) => '/dummy', + builder: (BuildContext context, GoRouterState state, + StatefulNavigationShell navigationShell) { + return navigationShell; + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/other', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/other2', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + ], + ), + ], + ), + GoRoute( + path: '/dummy', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ), + ]; + + final GoRouter router = await createRouter(routes, tester); + + for (final String shellRoute in ['/other', '/other2']) { + router.go(shellRoute); + await tester.pump(); + expect( + router.routerDelegate.currentConfiguration.uri.toString(), + '/dummy', + ); + } + }); }); group('initial location', () { From c99e6168db24af59fb31de72403a8362791fa94e Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Thu, 25 Apr 2024 14:42:46 -0300 Subject: [PATCH 7/8] chore: Fix go_router version --- packages/go_router/CHANGELOG.md | 2 +- packages/go_router/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 7a990db33ca..d8ff8bcde24 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,4 +1,4 @@ -## 14.0.1 +## 14.1.0 - Adds route redirect to ShellRoutes diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index bd53a5f18a2..f04e5433ae3 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -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.0.1 +version: 14.1.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 From 95b029e991ede4f88a998312d108a81d55839325 Mon Sep 17 00:00:00 2001 From: Victor Ohashi Date: Thu, 25 Apr 2024 14:44:02 -0300 Subject: [PATCH 8/8] chore: Fix go_router version --- packages/go_router/CHANGELOG.md | 2 +- packages/go_router/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 7a990db33ca..d8ff8bcde24 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,4 +1,4 @@ -## 14.0.1 +## 14.1.0 - Adds route redirect to ShellRoutes diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index bd53a5f18a2..f04e5433ae3 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -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.0.1 +version: 14.1.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