@@ -9,8 +9,8 @@ import '../go_router.dart';
99import 'configuration.dart' ;
1010import 'logging.dart' ;
1111import 'match.dart' ;
12- import 'matching.dart' ;
1312import 'misc/error_screen.dart' ;
13+ import 'misc/errors.dart' ;
1414import 'pages/cupertino.dart' ;
1515import 'pages/material.dart' ;
1616import 'route_data.dart' ;
@@ -83,7 +83,7 @@ class RouteBuilder {
8383 RouteMatchList matchList,
8484 bool routerNeglect,
8585 ) {
86- if (matchList.isEmpty) {
86+ if (matchList.isEmpty && ! matchList.isError ) {
8787 // The build method can be called before async redirect finishes. Build a
8888 // empty box until then.
8989 return const SizedBox .shrink ();
@@ -92,18 +92,12 @@ class RouteBuilder {
9292 context,
9393 Builder (
9494 builder: (BuildContext context) {
95- try {
96- final Map <Page <Object ?>, GoRouterState > newRegistry =
97- < Page <Object ?>, GoRouterState > {};
98- final Widget result = tryBuild (context, matchList, routerNeglect,
99- configuration.navigatorKey, newRegistry);
100- _registry.updateRegistry (newRegistry);
101- return GoRouterStateRegistryScope (
102- registry: _registry, child: result);
103- } on _RouteBuilderError catch (e) {
104- return _buildErrorNavigator (context, e, matchList.uri,
105- onPopPageWithRouteMatch, configuration.navigatorKey);
106- }
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);
107101 },
108102 ),
109103 );
@@ -147,28 +141,31 @@ class RouteBuilder {
147141 bool routerNeglect,
148142 GlobalKey <NavigatorState > navigatorKey,
149143 Map <Page <Object ?>, GoRouterState > registry) {
150- final Map <GlobalKey <NavigatorState >, List <Page <Object ?>>> keyToPage =
151- < GlobalKey <NavigatorState >, List <Page <Object ?>>> {};
152- 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 ?>>> {};
153154 _buildRecursive (context, matchList, 0 , pagePopContext, routerNeglect,
154155 keyToPage, navigatorKey, registry);
155156
156157 // Every Page should have a corresponding RouteMatch.
157158 assert (keyToPage.values.flattened.every ((Page <Object ?> page) =>
158159 pagePopContext.getRouteMatchForPage (page) != null ));
159- return keyToPage[navigatorKey]! ;
160- } on _RouteBuilderError catch (e) {
161- return < Page <Object ?>> [
162- _buildErrorPage (context, e, matchList.uri),
163- ];
164- } finally {
165- /// Clean up previous cache to prevent memory leak, making sure any nested
166- /// stateful shell routes for the current match list are kept.
167- final Set <Key > activeKeys = keyToPage.keys.toSet ()
168- ..addAll (_nestedStatefulNavigatorKeys (matchList));
169- _goHeroCache.removeWhere (
170- (GlobalKey <NavigatorState > key, _) => ! activeKeys.contains (key));
171160 }
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]! ;
172169 }
173170
174171 static Set <GlobalKey <NavigatorState >> _nestedStatefulNavigatorKeys (
@@ -200,15 +197,15 @@ class RouteBuilder {
200197 }
201198 final RouteMatch match = matchList.matches[startIndex];
202199
203- if (match.error != null ) {
204- throw _RouteBuilderError ('Match error found during build phase' ,
205- exception: match.error);
206- }
207-
208200 final RouteBase route = match.route;
209201 final GoRouterState state = buildState (matchList, match);
210202 Page <Object ?>? page;
211- 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 ) {
212209 page = _buildPageForGoRoute (context, state, match, route, pagePopContext);
213210 // If this GoRoute is for a different Navigator, add it to the
214211 // list of out of scope pages
@@ -284,7 +281,7 @@ class RouteBuilder {
284281 registry[page] = state;
285282 pagePopContext._setRouteMatchForPage (page, match);
286283 } else {
287- throw _RouteBuilderException ('Unsupported route type $route ' );
284+ throw GoError ('Unsupported route type $route ' );
288285 }
289286 }
290287
@@ -324,8 +321,17 @@ class RouteBuilder {
324321 name = route.name;
325322 path = route.path;
326323 }
327- final RouteMatchList effectiveMatchList =
328- 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+ }
329335 return GoRouterState (
330336 configuration,
331337 location: effectiveMatchList.uri.toString (),
@@ -335,10 +341,10 @@ class RouteBuilder {
335341 fullPath: effectiveMatchList.fullPath,
336342 pathParameters:
337343 Map <String , String >.from (effectiveMatchList.pathParameters),
338- error: match .error,
344+ error: effectiveMatchList .error,
339345 queryParameters: effectiveMatchList.uri.queryParameters,
340346 queryParametersAll: effectiveMatchList.uri.queryParametersAll,
341- extra: match .extra,
347+ extra: effectiveMatchList .extra,
342348 pageKey: match.pageKey,
343349 );
344350 }
@@ -370,7 +376,7 @@ class RouteBuilder {
370376 final GoRouterWidgetBuilder ? builder = route.builder;
371377
372378 if (builder == null ) {
373- throw _RouteBuilderError ('No routeBuilder provided to GoRoute: $route ' );
379+ throw GoError ('No routeBuilder provided to GoRoute: $route ' );
374380 }
375381
376382 return builder (context, state);
@@ -405,7 +411,7 @@ class RouteBuilder {
405411 final Widget ? widget =
406412 route.buildWidget (context, state, shellRouteContext! );
407413 if (widget == null ) {
408- throw _RouteBuilderError ('No builder provided to ShellRoute: $route ' );
414+ throw GoError ('No builder provided to ShellRoute: $route ' );
409415 }
410416
411417 return widget;
@@ -485,38 +491,26 @@ class RouteBuilder {
485491 child: child,
486492 );
487493
488- /// Builds a Navigator containing an error page.
489- Widget _buildErrorNavigator (
490- BuildContext context,
491- _RouteBuilderError e,
492- Uri uri,
493- PopPageWithRouteMatchCallback onPopPage,
494- GlobalKey <NavigatorState > navigatorKey) {
495- return _buildNavigator (
496- (Route <dynamic > route, dynamic result) => onPopPage (route, result, null ),
497- < Page <Object ?>> [
498- _buildErrorPage (context, e, uri),
499- ],
500- navigatorKey,
501- );
502- }
503-
504- /// Builds a an error page.
505- Page <void > _buildErrorPage (
506- BuildContext context,
507- _RouteBuilderError error,
494+ GoRouterState _buildErrorState (
495+ Exception error,
508496 Uri uri,
509497 ) {
510- final GoRouterState state = GoRouterState (
498+ final String location = uri.toString ();
499+ return GoRouterState (
511500 configuration,
512- location: uri. toString () ,
501+ location: location ,
513502 matchedLocation: uri.path,
514503 name: null ,
515504 queryParameters: uri.queryParameters,
516505 queryParametersAll: uri.queryParametersAll,
517- error: Exception ( error) ,
518- pageKey: const ValueKey <String >('error' ),
506+ error: error,
507+ pageKey: ValueKey <String >('$ location ( error) ' ),
519508 );
509+ }
510+
511+ /// Builds a an error page.
512+ Page <void > _buildErrorPage (BuildContext context, GoRouterState state) {
513+ assert (state.error != null );
520514
521515 // If the error page builder is provided, use that, otherwise, if the error
522516 // builder is provided, wrap that in an app-specific page (for example,
@@ -556,43 +550,6 @@ typedef _PageBuilderForAppType = Page<void> Function({
556550 required Widget child,
557551});
558552
559- /// An error that occurred while building the app's UI based on the route
560- /// matches.
561- class _RouteBuilderError extends Error {
562- /// Constructs a [_RouteBuilderError] .
563- _RouteBuilderError (this .message, {this .exception});
564-
565- /// The error message.
566- final String message;
567-
568- /// The exception that occurred.
569- final Exception ? exception;
570-
571- @override
572- String toString () {
573- return '$message ${exception ?? "" }' ;
574- }
575- }
576-
577- /// An error that occurred while building the app's UI based on the route
578- /// matches.
579- class _RouteBuilderException implements Exception {
580- /// Constructs a [_RouteBuilderException] .
581- //ignore: unused_element
582- _RouteBuilderException (this .message, {this .exception});
583-
584- /// The error message.
585- final String message;
586-
587- /// The exception that occurred.
588- final Exception ? exception;
589-
590- @override
591- String toString () {
592- return '$message ${exception ?? "" }' ;
593- }
594- }
595-
596553/// Context used to provide a route to page association when popping routes.
597554class _PagePopContext {
598555 _PagePopContext ._(this .onPopPageWithRouteMatch);
0 commit comments