@@ -10,6 +10,7 @@ import 'configuration.dart';
1010import 'logging.dart' ;
1111import 'match.dart' ;
1212import 'misc/error_screen.dart' ;
13+ import 'misc/errors.dart' ;
1314import 'pages/cupertino.dart' ;
1415import 'pages/material.dart' ;
1516import 'route_data.dart' ;
@@ -82,7 +83,7 @@ class RouteBuilder {
8283 RouteMatchList matchList,
8384 bool routerNeglect,
8485 ) {
85- if (matchList.isEmpty) {
86+ if (matchList.isEmpty && ! matchList.isError ) {
8687 // The build method can be called before async redirect finishes. Build a
8788 // empty box until then.
8889 return const SizedBox .shrink ();
@@ -91,18 +92,12 @@ class RouteBuilder {
9192 context,
9293 Builder (
9394 builder: (BuildContext context) {
94- try {
95- final Map <Page <Object ?>, GoRouterState > newRegistry =
96- < Page <Object ?>, GoRouterState > {};
97- final Widget result = tryBuild (context, matchList, routerNeglect,
98- configuration.navigatorKey, newRegistry);
99- _registry.updateRegistry (newRegistry);
100- return GoRouterStateRegistryScope (
101- registry: _registry, child: result);
102- } on _RouteBuilderError catch (e) {
103- return _buildErrorNavigator (context, e, matchList.uri,
104- onPopPageWithRouteMatch, configuration.navigatorKey);
105- }
95+ final Map <Page <Object ?>, GoRouterState > newRegistry =
96+ < Page <Object ?>, GoRouterState > {};
97+ final Widget result = tryBuild (context, matchList, routerNeglect,
98+ configuration.navigatorKey, newRegistry);
99+ _registry.updateRegistry (newRegistry);
100+ return GoRouterStateRegistryScope (registry: _registry, child: result);
106101 },
107102 ),
108103 );
@@ -146,28 +141,31 @@ class RouteBuilder {
146141 bool routerNeglect,
147142 GlobalKey <NavigatorState > navigatorKey,
148143 Map <Page <Object ?>, GoRouterState > registry) {
149- final Map <GlobalKey <NavigatorState >, List <Page <Object ?>>> keyToPage =
150- < GlobalKey <NavigatorState >, List <Page <Object ?>>> {};
151- try {
144+ final Map <GlobalKey <NavigatorState >, List <Page <Object ?>>> keyToPage;
145+ if (matchList.isError) {
146+ keyToPage = < GlobalKey <NavigatorState >, List <Page <Object ?>>> {
147+ navigatorKey: < Page <Object ?>> [
148+ _buildErrorPage (
149+ context, _buildErrorState (matchList.error! , matchList.uri)),
150+ ]
151+ };
152+ } else {
153+ keyToPage = < GlobalKey <NavigatorState >, List <Page <Object ?>>> {};
152154 _buildRecursive (context, matchList, 0 , pagePopContext, routerNeglect,
153155 keyToPage, navigatorKey, registry);
154156
155157 // Every Page should have a corresponding RouteMatch.
156158 assert (keyToPage.values.flattened.every ((Page <Object ?> page) =>
157159 pagePopContext.getRouteMatchForPage (page) != null ));
158- return keyToPage[navigatorKey]! ;
159- } on _RouteBuilderError catch (e) {
160- return < Page <Object ?>> [
161- _buildErrorPage (context, e, matchList.uri),
162- ];
163- } finally {
164- /// Clean up previous cache to prevent memory leak, making sure any nested
165- /// stateful shell routes for the current match list are kept.
166- final Set <Key > activeKeys = keyToPage.keys.toSet ()
167- ..addAll (_nestedStatefulNavigatorKeys (matchList));
168- _goHeroCache.removeWhere (
169- (GlobalKey <NavigatorState > key, _) => ! activeKeys.contains (key));
170160 }
161+
162+ /// Clean up previous cache to prevent memory leak, making sure any nested
163+ /// stateful shell routes for the current match list are kept.
164+ final Set <Key > activeKeys = keyToPage.keys.toSet ()
165+ ..addAll (_nestedStatefulNavigatorKeys (matchList));
166+ _goHeroCache.removeWhere (
167+ (GlobalKey <NavigatorState > key, _) => ! activeKeys.contains (key));
168+ return keyToPage[navigatorKey]! ;
171169 }
172170
173171 static Set <GlobalKey <NavigatorState >> _nestedStatefulNavigatorKeys (
@@ -199,15 +197,15 @@ class RouteBuilder {
199197 }
200198 final RouteMatch match = matchList.matches[startIndex];
201199
202- if (match.error != null ) {
203- throw _RouteBuilderError ('Match error found during build phase' ,
204- exception: match.error);
205- }
206-
207200 final RouteBase route = match.route;
208201 final GoRouterState state = buildState (matchList, match);
209202 Page <Object ?>? page;
210- if (route is GoRoute ) {
203+ if (state.error != null ) {
204+ page = _buildErrorPage (context, state);
205+ keyToPages.putIfAbsent (navigatorKey, () => < Page <Object ?>> []).add (page);
206+ _buildRecursive (context, matchList, startIndex + 1 , pagePopContext,
207+ routerNeglect, keyToPages, navigatorKey, registry);
208+ } else if (route is GoRoute ) {
211209 page = _buildPageForGoRoute (context, state, match, route, pagePopContext);
212210 // If this GoRoute is for a different Navigator, add it to the
213211 // list of out of scope pages
@@ -283,7 +281,7 @@ class RouteBuilder {
283281 registry[page] = state;
284282 pagePopContext._setRouteMatchForPage (page, match);
285283 } else {
286- throw _RouteBuilderException ('Unsupported route type $route ' );
284+ throw GoError ('Unsupported route type $route ' );
287285 }
288286 }
289287
@@ -323,8 +321,17 @@ class RouteBuilder {
323321 name = route.name;
324322 path = route.path;
325323 }
326- final RouteMatchList effectiveMatchList =
327- match is ImperativeRouteMatch ? match.matches : matchList;
324+ final RouteMatchList effectiveMatchList;
325+ if (match is ImperativeRouteMatch ) {
326+ effectiveMatchList = match.matches;
327+ if (effectiveMatchList.isError) {
328+ return _buildErrorState (
329+ effectiveMatchList.error! , effectiveMatchList.uri);
330+ }
331+ } else {
332+ effectiveMatchList = matchList;
333+ assert (! effectiveMatchList.isError);
334+ }
328335 return GoRouterState (
329336 configuration,
330337 location: effectiveMatchList.uri.toString (),
@@ -334,10 +341,10 @@ class RouteBuilder {
334341 fullPath: effectiveMatchList.fullPath,
335342 pathParameters:
336343 Map <String , String >.from (effectiveMatchList.pathParameters),
337- error: match .error,
344+ error: effectiveMatchList .error,
338345 queryParameters: effectiveMatchList.uri.queryParameters,
339346 queryParametersAll: effectiveMatchList.uri.queryParametersAll,
340- extra: match .extra,
347+ extra: effectiveMatchList .extra,
341348 pageKey: match.pageKey,
342349 );
343350 }
@@ -369,7 +376,7 @@ class RouteBuilder {
369376 final GoRouterWidgetBuilder ? builder = route.builder;
370377
371378 if (builder == null ) {
372- throw _RouteBuilderError ('No routeBuilder provided to GoRoute: $route ' );
379+ throw GoError ('No routeBuilder provided to GoRoute: $route ' );
373380 }
374381
375382 return builder (context, state);
@@ -404,7 +411,7 @@ class RouteBuilder {
404411 final Widget ? widget =
405412 route.buildWidget (context, state, shellRouteContext! );
406413 if (widget == null ) {
407- throw _RouteBuilderError ('No builder provided to ShellRoute: $route ' );
414+ throw GoError ('No builder provided to ShellRoute: $route ' );
408415 }
409416
410417 return widget;
@@ -484,38 +491,26 @@ class RouteBuilder {
484491 child: child,
485492 );
486493
487- /// Builds a Navigator containing an error page.
488- Widget _buildErrorNavigator (
489- BuildContext context,
490- _RouteBuilderError e,
491- Uri uri,
492- PopPageWithRouteMatchCallback onPopPage,
493- GlobalKey <NavigatorState > navigatorKey) {
494- return _buildNavigator (
495- (Route <dynamic > route, dynamic result) => onPopPage (route, result, null ),
496- < Page <Object ?>> [
497- _buildErrorPage (context, e, uri),
498- ],
499- navigatorKey,
500- );
501- }
502-
503- /// Builds a an error page.
504- Page <void > _buildErrorPage (
505- BuildContext context,
506- _RouteBuilderError error,
494+ GoRouterState _buildErrorState (
495+ Exception error,
507496 Uri uri,
508497 ) {
509- final GoRouterState state = GoRouterState (
498+ final String location = uri.toString ();
499+ return GoRouterState (
510500 configuration,
511- location: uri. toString () ,
501+ location: location ,
512502 matchedLocation: uri.path,
513503 name: null ,
514504 queryParameters: uri.queryParameters,
515505 queryParametersAll: uri.queryParametersAll,
516- error: Exception ( error) ,
517- pageKey: const ValueKey <String >('error' ),
506+ error: error,
507+ pageKey: ValueKey <String >('$ location ( error) ' ),
518508 );
509+ }
510+
511+ /// Builds a an error page.
512+ Page <void > _buildErrorPage (BuildContext context, GoRouterState state) {
513+ assert (state.error != null );
519514
520515 // If the error page builder is provided, use that, otherwise, if the error
521516 // builder is provided, wrap that in an app-specific page (for example,
@@ -555,42 +550,6 @@ typedef _PageBuilderForAppType = Page<void> Function({
555550 required Widget child,
556551});
557552
558- /// An error that occurred while building the app's UI based on the route
559- /// matches.
560- class _RouteBuilderError extends Error {
561- /// Constructs a [_RouteBuilderError] .
562- _RouteBuilderError (this .message, {this .exception});
563-
564- /// The error message.
565- final String message;
566-
567- /// The exception that occurred.
568- final Exception ? exception;
569-
570- @override
571- String toString () {
572- return '$message ${exception ?? "" }' ;
573- }
574- }
575-
576- /// An error that occurred while building the app's UI based on the route
577- /// matches.
578- class _RouteBuilderException implements Exception {
579- /// Constructs a [_RouteBuilderException] .
580- _RouteBuilderException (this .message, {this .exception});
581-
582- /// The error message.
583- final String message;
584-
585- /// The exception that occurred.
586- final Exception ? exception;
587-
588- @override
589- String toString () {
590- return '$message ${exception ?? "" }' ;
591- }
592- }
593-
594553/// Context used to provide a route to page association when popping routes.
595554class _PagePopContext {
596555 _PagePopContext ._(this .onPopPageWithRouteMatch);
0 commit comments