Skip to content

Commit ed16a13

Browse files
committed
update
1 parent 76f2878 commit ed16a13

15 files changed

+271
-274
lines changed

packages/go_router/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ See the API documentation for details on the following topics:
3737
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)
3838

3939
## Migration guides
40-
- [Migrating to 7.0.0](https://docs.google.com/document/d/10Xbpifbs4E-zh6YE5akIO8raJq_m3FIXs6nUGdOspOg).
40+
- [Migrating to 8.0.0](https://flutter.dev/go/go-router-v8-breaking-changes).
41+
- [Migrating to 7.0.0](https://flutter.dev/go/go-router-v7-breaking-changes).
4142
- [Migrating to 6.0.0](https://flutter.dev/go/go-router-v6-breaking-changes)
4243
- [Migrating to 5.1.2](https://flutter.dev/go/go-router-v5-1-2-breaking-changes)
4344
- [Migrating to 5.0](https://flutter.dev/go/go-router-v5-breaking-changes)

packages/go_router/lib/src/builder.dart

Lines changed: 60 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'configuration.dart';
1010
import 'logging.dart';
1111
import 'match.dart';
1212
import 'misc/error_screen.dart';
13+
import 'misc/errors.dart';
1314
import 'pages/cupertino.dart';
1415
import 'pages/material.dart';
1516
import '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.
595554
class _PagePopContext {
596555
_PagePopContext._(this.onPopPageWithRouteMatch);

packages/go_router/lib/src/configuration.dart

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,8 @@ class RouteConfiguration {
187187
static RouteMatchList _errorRouteMatchList(Uri uri, String errorMessage) {
188188
final Exception error = Exception(errorMessage);
189189
return RouteMatchList(
190-
matches: <RouteMatch>[
191-
RouteMatch(
192-
matchedLocation: uri.path,
193-
extra: null,
194-
error: error,
195-
route: GoRoute(
196-
path: uri.toString(),
197-
pageBuilder: (BuildContext context, GoRouterState state) {
198-
throw UnimplementedError();
199-
},
200-
),
201-
pageKey: const ValueKey<String>('error'),
202-
),
203-
],
190+
matches: const <RouteMatch>[],
191+
error: error,
204192
uri: uri,
205193
pathParameters: const <String, String>{},
206194
);
@@ -267,25 +255,26 @@ class RouteConfiguration {
267255
final Uri uri = Uri.parse(canonicalUri(location));
268256

269257
final Map<String, String> pathParameters = <String, String>{};
270-
final List<RouteMatch>? matches =
271-
_getLocRouteMatches(uri, extra, pathParameters);
258+
final List<RouteMatch>? matches = _getLocRouteMatches(uri, pathParameters);
272259

273260
if (matches == null) {
274261
return _errorRouteMatchList(uri, 'no routes for location: $uri');
275262
}
276263
return RouteMatchList(
277-
matches: matches, uri: uri, pathParameters: pathParameters);
264+
matches: matches,
265+
uri: uri,
266+
pathParameters: pathParameters,
267+
extra: extra);
278268
}
279269

280270
List<RouteMatch>? _getLocRouteMatches(
281-
Uri uri, Object? extra, Map<String, String> pathParameters) {
271+
Uri uri, Map<String, String> pathParameters) {
282272
final List<RouteMatch>? result = _getLocRouteRecursively(
283273
location: uri.path,
284274
remainingLocation: uri.path,
285275
matchedLocation: '',
286276
pathParameters: pathParameters,
287277
routes: routes,
288-
extra: extra,
289278
);
290279
return result;
291280
}
@@ -296,7 +285,6 @@ class RouteConfiguration {
296285
required String matchedLocation,
297286
required Map<String, String> pathParameters,
298287
required List<RouteBase> routes,
299-
required Object? extra,
300288
}) {
301289
List<RouteMatch>? result;
302290
late Map<String, String> subPathParameters;
@@ -309,7 +297,6 @@ class RouteConfiguration {
309297
remainingLocation: remainingLocation,
310298
matchedLocation: matchedLocation,
311299
pathParameters: subPathParameters,
312-
extra: extra,
313300
);
314301

315302
if (match == null) {
@@ -347,7 +334,6 @@ class RouteConfiguration {
347334
matchedLocation: newParentSubLoc,
348335
pathParameters: subPathParameters,
349336
routes: route.routes,
350-
extra: extra,
351337
);
352338

353339
// If there's no sub-route matches, there is no match for this location
@@ -369,7 +355,7 @@ class RouteConfiguration {
369355
/// location.
370356
FutureOr<RouteMatchList> redirect(
371357
BuildContext context, FutureOr<RouteMatchList> prevMatchListFuture,
372-
{required List<RouteMatchList> redirectHistory, Object? extra}) {
358+
{required List<RouteMatchList> redirectHistory}) {
373359
FutureOr<RouteMatchList> processRedirect(RouteMatchList prevMatchList) {
374360
final String prevLocation = prevMatchList.uri.toString();
375361
FutureOr<RouteMatchList> processTopLevelRedirect(
@@ -388,7 +374,6 @@ class RouteConfiguration {
388374
context,
389375
newMatch,
390376
redirectHistory: redirectHistory,
391-
extra: extra,
392377
);
393378
}
394379

@@ -409,7 +394,6 @@ class RouteConfiguration {
409394
context,
410395
newMatch,
411396
redirectHistory: redirectHistory,
412-
extra: extra,
413397
);
414398
}
415399
return prevMatchList;
@@ -437,7 +421,7 @@ class RouteConfiguration {
437421
matchedLocation: prevMatchList.uri.path,
438422
queryParameters: prevMatchList.uri.queryParameters,
439423
queryParametersAll: prevMatchList.uri.queryParametersAll,
440-
extra: extra,
424+
extra: prevMatchList.extra,
441425
pageKey: const ValueKey<String>('topLevel'),
442426
),
443427
);
@@ -478,7 +462,7 @@ class RouteConfiguration {
478462
name: route.name,
479463
path: route.path,
480464
fullPath: matchList.fullPath,
481-
extra: match.extra,
465+
extra: matchList.extra,
482466
pathParameters: matchList.pathParameters,
483467
queryParameters: matchList.uri.queryParameters,
484468
queryParametersAll: matchList.uri.queryParametersAll,

0 commit comments

Comments
 (0)