mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +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
|
## 3.1.0
|
||||||
|
|
||||||
- Added `GoRouteData` and `TypedGoRoute` to support `package:go_router_builder`.
|
- 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;
|
final List<GoRoute> routes;
|
||||||
|
|
||||||
/// An optional redirect function for this route.
|
/// An optional redirect function for this route.
|
||||||
|
@ -441,30 +441,10 @@ class GoRouterDelegate extends RouterDelegate<Uri>
|
|||||||
).toList();
|
).toList();
|
||||||
|
|
||||||
assert(matchStacks.isNotEmpty, 'no routes for location: $location');
|
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) {
|
// If there are multiple routes that match the location, returning the first one.
|
||||||
sb.writeln(
|
// To make predefined routes to take precedence over dynamic routes eg. '/:id'
|
||||||
'\t${stack.map((GoRouteMatch m) => m.route.path).join(' => ')}');
|
// consider adding the dynamic route at the end of the routes
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}());
|
|
||||||
|
|
||||||
return matchStacks.first;
|
return matchStacks.first;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: go_router
|
name: go_router
|
||||||
description: A declarative router for Flutter based on Navigation 2 supporting
|
description: A declarative router for Flutter based on Navigation 2 supporting
|
||||||
deep linking, data-driven routes and more
|
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
|
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
|
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);
|
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>[
|
final List<GoRoute> routes = <GoRoute>[
|
||||||
GoRoute(path: '/', builder: _dummy),
|
GoRoute(path: '/', builder: _dummy),
|
||||||
GoRoute(path: '/', builder: _dummy),
|
GoRoute(path: '/', builder: _dummy),
|
||||||
@ -50,7 +50,7 @@ void main() {
|
|||||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||||
expect(matches, hasLength(1));
|
expect(matches, hasLength(1));
|
||||||
expect(matches.first.fullpath, '/');
|
expect(matches.first.fullpath, '/');
|
||||||
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
|
expect(router.screenFor(matches.first).runtimeType, DummyScreen);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('empty path', () {
|
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>[
|
final List<GoRoute> routes = <GoRoute>[
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
builder: _dummy,
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
|
const HomeScreen(),
|
||||||
routes: <GoRoute>[
|
routes: <GoRoute>[
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'foo/bar',
|
path: 'foo/bar',
|
||||||
builder: _dummy,
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
|
const FamilyScreen(''),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'bar',
|
||||||
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
|
const Page1Screen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'foo',
|
path: 'foo',
|
||||||
builder: _dummy,
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
|
const Page2Screen(),
|
||||||
routes: <GoRoute>[
|
routes: <GoRoute>[
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'bar',
|
path: 'bar',
|
||||||
builder: _dummy,
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
|
const LoginScreen(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -300,10 +309,20 @@ void main() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
final GoRouter router = _router(routes);
|
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');
|
router.go('/foo/bar');
|
||||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
matches = router.routerDelegate.matches;
|
||||||
expect(matches, hasLength(1));
|
expect(matches, hasLength(2));
|
||||||
expect(router.screenFor(matches.first).runtimeType, ErrorScreen);
|
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', () {
|
test('router state', () {
|
||||||
@ -424,17 +443,19 @@ void main() {
|
|||||||
expect(router.screenFor(matches.first).runtimeType, FamilyScreen);
|
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>[
|
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: '/page1', builder: _dummy),
|
||||||
|
GoRoute(path: '/:ok', builder: _dummy),
|
||||||
];
|
];
|
||||||
|
|
||||||
final GoRouter router = _router(routes);
|
final GoRouter router = _router(routes);
|
||||||
router.go('/PAGE1');
|
router.go('/user');
|
||||||
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
final List<GoRouteMatch> matches = router.routerDelegate.matches;
|
||||||
expect(matches, hasLength(1));
|
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