mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 23:51:55 +08:00
[go_router] If there is more than one route to match, use the first match. (#1995)
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 3.1.1
|
||||
|
||||
- Uses first match if there are more than one route to match. [ [#99833](https://github.com/flutter/flutter/issues/99833)
|
||||
|
||||
## 3.1.0
|
||||
|
||||
- Added `GoRouteData` and `TypedGoRoute` to support `package:go_router_builder`.
|
||||
|
@ -162,6 +162,30 @@ class GoRoute {
|
||||
/// ],
|
||||
/// );
|
||||
///
|
||||
/// If there are multiple routes that match the location, the first match is used.
|
||||
/// To make predefined routes to take precedence over dynamic routes eg. '/:id'
|
||||
/// consider adding the dynamic route at the end of the routes
|
||||
/// For example:
|
||||
/// ```
|
||||
/// final GoRouter _router = GoRouter(
|
||||
/// routes: <GoRoute>[
|
||||
/// GoRoute(
|
||||
/// path: '/',
|
||||
/// redirect: (_) => '/family/${Families.data[0].id}',
|
||||
/// ),
|
||||
/// GoRoute(
|
||||
/// path: '/family',
|
||||
/// pageBuilder: (BuildContext context, GoRouterState state) => ...,
|
||||
/// ),
|
||||
/// GoRoute(
|
||||
/// path: '/:username',
|
||||
/// pageBuilder: (BuildContext context, GoRouterState state) => ...,
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// ```
|
||||
/// In the above example, if /family route is matched, it will be used.
|
||||
/// else /:username route will be used.
|
||||
final List<GoRoute> routes;
|
||||
|
||||
/// An optional redirect function for this route.
|
||||
|
@ -441,30 +441,10 @@ class GoRouterDelegate extends RouterDelegate<Uri>
|
||||
).toList();
|
||||
|
||||
assert(matchStacks.isNotEmpty, 'no routes for location: $location');
|
||||
assert(() {
|
||||
if (matchStacks.length > 1) {
|
||||
final StringBuffer sb = StringBuffer()
|
||||
..writeln('too many routes for location: $location');
|
||||
|
||||
for (final List<GoRouteMatch> stack in matchStacks) {
|
||||
sb.writeln(
|
||||
'\t${stack.map((GoRouteMatch m) => m.route.path).join(' => ')}');
|
||||
}
|
||||
|
||||
assert(false, sb.toString());
|
||||
}
|
||||
|
||||
assert(matchStacks.length == 1);
|
||||
final GoRouteMatch match = matchStacks.first.last;
|
||||
final String loc1 = _addQueryParams(match.subloc, match.queryParams);
|
||||
final Uri uri2 = Uri.parse(location);
|
||||
final String loc2 = _addQueryParams(uri2.path, uri2.queryParameters);
|
||||
|
||||
// NOTE: match the lower case, since subloc is canonicalized to match the
|
||||
// path case whereas the location can be any case
|
||||
assert(loc1.toLowerCase() == loc2.toLowerCase(), '$loc1 != $loc2');
|
||||
return true;
|
||||
}());
|
||||
// If there are multiple routes that match the location, returning the first one.
|
||||
// To make predefined routes to take precedence over dynamic routes eg. '/:id'
|
||||
// consider adding the dynamic route at the end of the routes
|
||||
|
||||
return matchStacks.first;
|
||||
}
|
||||
|
@ -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: 3.1.0
|
||||
version: 3.1.1
|
||||
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
|
||||
|
||||
|
@ -39,7 +39,7 @@ void main() {
|
||||
expect(router.screenFor(matches.first).runtimeType, HomeScreen);
|
||||
});
|
||||
|
||||
test('match too many routes', () {
|
||||
test('If there is more than one route to match, use the first match', () {
|
||||
final List<GoRoute> routes = <GoRoute>[
|
||||
GoRoute(path: '/', builder: _dummy),
|
||||
GoRoute(path: '/', builder: _dummy),
|
||||
@ -50,7 +50,7 @@ void main() {
|
||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(1));
|
||||
expect(matches.first.fullpath, '/');
|
||||
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
|
||||
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
|
||||
});
|
||||
|
||||
test('empty path', () {
|
||||
@ -275,23 +275,32 @@ void main() {
|
||||
}
|
||||
});
|
||||
|
||||
test('match too many sub-routes', () {
|
||||
test('return first matching route if too many subroutes', () {
|
||||
final List<GoRoute> routes = <GoRoute>[
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: _dummy,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const HomeScreen(),
|
||||
routes: <GoRoute>[
|
||||
GoRoute(
|
||||
path: 'foo/bar',
|
||||
builder: _dummy,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const FamilyScreen(''),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'bar',
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const Page1Screen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'foo',
|
||||
builder: _dummy,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const Page2Screen(),
|
||||
routes: <GoRoute>[
|
||||
GoRoute(
|
||||
path: 'bar',
|
||||
builder: _dummy,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const LoginScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -300,10 +309,20 @@ void main() {
|
||||
];
|
||||
|
||||
final GoRouter router = _router(routes);
|
||||
router.go('/bar');
|
||||
List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(2));
|
||||
expect(router.screenFor(matches[1]).runtimeType, Page1Screen);
|
||||
|
||||
router.go('/foo/bar');
|
||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(1));
|
||||
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
|
||||
matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(2));
|
||||
expect(router.screenFor(matches[1]).runtimeType, FamilyScreen);
|
||||
|
||||
router.go('/foo');
|
||||
matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(2));
|
||||
expect(router.screenFor(matches[1]).runtimeType, Page2Screen);
|
||||
});
|
||||
|
||||
test('router state', () {
|
||||
@ -424,17 +443,19 @@ void main() {
|
||||
expect(router.screenFor(matches.first).runtimeType, FamilyScreen);
|
||||
});
|
||||
|
||||
test('match too many routes, ignoring case', () {
|
||||
test('If there is more than one route to match, use the first match.', () {
|
||||
final List<GoRoute> routes = <GoRoute>[
|
||||
GoRoute(path: '/', builder: _dummy),
|
||||
GoRoute(path: '/page1', builder: _dummy),
|
||||
GoRoute(path: '/PaGe1', builder: _dummy),
|
||||
GoRoute(path: '/page1', builder: _dummy),
|
||||
GoRoute(path: '/:ok', builder: _dummy),
|
||||
];
|
||||
|
||||
final GoRouter router = _router(routes);
|
||||
router.go('/PAGE1');
|
||||
router.go('/user');
|
||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||
expect(matches, hasLength(1));
|
||||
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
|
||||
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user