[go_router] Add GoRouterState parameters to GoRouterData and rename replace methods (#2848)

*  Add redirectWithState method to GoRouteData

*  Update the tests

* ⬆️ Increase version number

* ✏️ Better changelog

* 💥 Remove xxxWithState and adds a the context and the state as a parameter to all GoRouteData callbacks

* 📝 Update the change logs and version number

* 💥 Rename replace into pushReplacement

* 📝 Add pushReplacementNamed into change log

* 📝 Add migration guide's link to the read me
This commit is contained in:
Valentin Vignal
2022-12-20 06:46:16 +08:00
committed by GitHub
parent 9b153b2495
commit 1cabcfb44b
9 changed files with 87 additions and 61 deletions

View File

@ -1,3 +1,13 @@
## 6.0.0
- **BREAKING CHANGE**
- `GoRouteData`'s `redirect` now takes 2 parameters `BuildContext context, GoRouterState state`.
- `GoRouteData`'s `build` now takes 2 parameters `BuildContext context, GoRouterState state`.
- `GoRouteData`'s `buildPageWithState` has been removed and replaced by `buildPage` with now takes 2 parameters `BuildContext context, GoRouterState state`.
- `replace` from `GoRouter`, `GoRouterDelegate` and `GoRouterHelper` has been renamed into `pushReplacement`.
- `replaceNamed` from `GoRouter`, `GoRouterDelegate` and `GoRouterHelper` has been renamed into `pushReplacementNamed`.
- [go_router v6 migration guide](https://flutter.dev/go/go-router-v6-breaking-changes)
## 5.2.4
- Fixes crashes when using async redirect.

View File

@ -37,6 +37,7 @@ See the API documentation for details on the following topics:
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)
## Migration guides
- [Migrating to 6.0.0](https://flutter.dev/go/go-router-v6-breaking-changes)
- [Migrating to 5.1.2](https://flutter.dev/go/go-router-v5-1-2-breaking-changes)
- [Migrating to 5.0](https://flutter.dev/go/go-router-v5-breaking-changes)
- [Migrating to 4.0](https://flutter.dev/go/go-router-v4-breaking-changes)

View File

@ -145,7 +145,7 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
///
/// See also:
/// * [push] which pushes the given location onto the page stack.
void replace(RouteMatchList matches) {
void pushReplacement(RouteMatchList matches) {
_matchList.pop();
push(matches); // [push] will notify the listeners.
}

View File

@ -67,8 +67,8 @@ extension GoRouterHelper on BuildContext {
/// See also:
/// * [go] which navigates to the location.
/// * [push] which pushes the location onto the page stack.
void replace(String location, {Object? extra}) =>
GoRouter.of(this).replace(location, extra: extra);
void pushReplacement(String location, {Object? extra}) =>
GoRouter.of(this).pushReplacement(location, extra: extra);
/// Replaces the top-most page of the page stack with the named route w/
/// optional parameters, e.g. `name='person', params={'fid': 'f2', 'pid':
@ -77,13 +77,13 @@ extension GoRouterHelper on BuildContext {
/// See also:
/// * [goNamed] which navigates a named route.
/// * [pushNamed] which pushes a named route onto the page stack.
void replaceNamed(
void pushReplacementNamed(
String name, {
Map<String, String> params = const <String, String>{},
Map<String, dynamic> queryParams = const <String, dynamic>{},
Object? extra,
}) =>
GoRouter.of(this).replaceNamed(
GoRouter.of(this).pushReplacementNamed(
name,
params: params,
queryParams: queryParams,

View File

@ -14,7 +14,7 @@ import 'state.dart';
/// Baseclass for supporting
/// [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).
///
/// Subclasses must override one of [build], [buildPageWithState], or
/// Subclasses must override one of [build], [buildPage], or
/// [redirect].
/// {@category Type-safe routes}
abstract class GoRouteData {
@ -25,53 +25,36 @@ abstract class GoRouteData {
/// Creates the [Widget] for `this` route.
///
/// Subclasses must override one of [build], [buildPageWithState], or
/// Subclasses must override one of [build], [buildPage], or
/// [redirect].
///
/// Corresponds to [GoRoute.builder].
Widget build(BuildContext context) => throw UnimplementedError(
'One of `build` or `buildPageWithState` must be implemented.',
Widget build(BuildContext context, GoRouterState state) =>
throw UnimplementedError(
'One of `build` or `buildPage` must be implemented.',
);
/// A page builder for this route.
///
/// Subclasses can override this function to provide a custom [Page].
///
/// Subclasses must override one of [build], [buildPageWithState] or
/// Subclasses must override one of [build], [buildPage] or
/// [redirect].
///
/// Corresponds to [GoRoute.pageBuilder].
///
/// By default, returns a [Page] instance that is ignored, causing a default
/// [Page] implementation to be used with the results of [build].
@Deprecated(
'This method has been deprecated in favor of buildPageWithState. '
'This feature was deprecated after v4.3.0.',
)
Page<void> buildPage(BuildContext context) => const NoOpPage();
/// A page builder for this route with [GoRouterState].
///
/// Subclasses can override this function to provide a custom [Page].
///
/// Subclasses must override one of [build], [buildPageWithState] or
/// [redirect].
///
/// Corresponds to [GoRoute.pageBuilder].
///
/// By default, returns a [Page] instance that is ignored, causing a default
/// [Page] implementation to be used with the results of [build].
Page<void> buildPageWithState(BuildContext context, GoRouterState state) =>
// ignore: deprecated_member_use_from_same_package
buildPage(context);
Page<void> buildPage(BuildContext context, GoRouterState state) =>
const NoOpPage();
/// An optional redirect function for this route.
///
/// Subclasses must override one of [build], [buildPageWithState], or
/// Subclasses must override one of [build], [buildPage], or
/// [redirect].
///
/// Corresponds to [GoRoute.redirect].
FutureOr<String?> redirect() => null;
FutureOr<String?> redirect(BuildContext context, GoRouterState state) => null;
/// A helper function used by generated code.
///
@ -106,13 +89,13 @@ abstract class GoRouteData {
}
Widget builder(BuildContext context, GoRouterState state) =>
factoryImpl(state).build(context);
factoryImpl(state).build(context, state);
Page<void> pageBuilder(BuildContext context, GoRouterState state) =>
factoryImpl(state).buildPageWithState(context, state);
factoryImpl(state).buildPage(context, state);
FutureOr<String?> redirect(BuildContext context, GoRouterState state) =>
factoryImpl(state).redirect();
factoryImpl(state).redirect(context, state);
return GoRoute(
path: path,

View File

@ -240,7 +240,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
/// See also:
/// * [go] which navigates to the location.
/// * [push] which pushes the location onto the page stack.
void replace(String location, {Object? extra}) {
void pushReplacement(String location, {Object? extra}) {
routeInformationParser
.parseRouteInformationWithDependencies(
RouteInformation(location: location, state: extra),
@ -249,7 +249,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
_routerDelegate.navigatorKey.currentContext!,
)
.then<void>((RouteMatchList matchList) {
routerDelegate.replace(matchList);
routerDelegate.pushReplacement(matchList);
});
}
@ -260,13 +260,13 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
/// See also:
/// * [goNamed] which navigates a named route.
/// * [pushNamed] which pushes a named route onto the page stack.
void replaceNamed(
void pushReplacementNamed(
String name, {
Map<String, String> params = const <String, String>{},
Map<String, dynamic> queryParams = const <String, dynamic>{},
Object? extra,
}) {
replace(
pushReplacement(
namedLocation(name, params: params, queryParams: queryParams),
extra: extra,
);

View File

@ -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: 5.2.4
version: 6.0.0
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

View File

@ -106,7 +106,7 @@ void main() {
);
});
group('replace', () {
group('pushReplacement', () {
testWidgets('It should replace the last match with the given one',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
@ -128,7 +128,7 @@ void main() {
goRouter.routerDelegate.addListener(expectAsync0(() {}));
final RouteMatch first = goRouter.routerDelegate.matches.matches.first;
final RouteMatch last = goRouter.routerDelegate.matches.last;
goRouter.replace('/page-1');
goRouter.pushReplacement('/page-1');
expect(goRouter.routerDelegate.matches.matches.length, 2);
expect(
goRouter.routerDelegate.matches.matches.first,
@ -169,7 +169,7 @@ void main() {
const Key('/a-p1'),
);
goRouter.replace('/a');
goRouter.pushReplacement('/a');
await tester.pumpAndSettle();
expect(goRouter.routerDelegate.matches.matches.length, 2);
@ -181,7 +181,7 @@ void main() {
);
});
group('replaceNamed', () {
group('pushReplacementNamed', () {
testWidgets(
'It should replace the last match with the given one',
(WidgetTester tester) async {
@ -210,7 +210,7 @@ void main() {
goRouter.routerDelegate.addListener(expectAsync0(() {}));
final RouteMatch first = goRouter.routerDelegate.matches.matches.first;
final RouteMatch last = goRouter.routerDelegate.matches.last;
goRouter.replaceNamed('page1');
goRouter.pushReplacementNamed('page1');
expect(goRouter.routerDelegate.matches.matches.length, 2);
expect(
goRouter.routerDelegate.matches.matches.first,

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
@ -9,7 +11,8 @@ import 'package:go_router/go_router.dart';
class _GoRouteDataBuild extends GoRouteData {
const _GoRouteDataBuild();
@override
Widget build(BuildContext context) => const SizedBox(key: Key('build'));
Widget build(BuildContext context, GoRouterState state) =>
const SizedBox(key: Key('build'));
}
final GoRoute _goRouteDataBuild = GoRouteData.$route(
@ -20,7 +23,8 @@ final GoRoute _goRouteDataBuild = GoRouteData.$route(
class _GoRouteDataBuildPage extends GoRouteData {
const _GoRouteDataBuildPage();
@override
Page<void> buildPage(BuildContext context) => const MaterialPage<void>(
Page<void> buildPage(BuildContext context, GoRouterState state) =>
const MaterialPage<void>(
child: SizedBox(key: Key('buildPage')),
);
}
@ -30,24 +34,22 @@ final GoRoute _goRouteDataBuildPage = GoRouteData.$route(
factory: (GoRouterState state) => const _GoRouteDataBuildPage(),
);
class _GoRouteDataBuildPageWithState extends GoRouteData {
const _GoRouteDataBuildPageWithState();
class _GoRouteDataRedirectPage extends GoRouteData {
const _GoRouteDataRedirectPage();
@override
Page<void> buildPageWithState(BuildContext context, GoRouterState state) =>
const MaterialPage<void>(
child: SizedBox(key: Key('buildPageWithState')),
);
FutureOr<String> redirect(BuildContext context, GoRouterState state) =>
'/build-page';
}
final GoRoute _goRouteDataBuildPageWithState = GoRouteData.$route(
path: '/build-page-with-state',
factory: (GoRouterState state) => const _GoRouteDataBuildPageWithState(),
final GoRoute _goRouteDataRedirect = GoRouteData.$route(
path: '/redirect',
factory: (GoRouterState state) => const _GoRouteDataRedirectPage(),
);
final List<GoRoute> _routes = <GoRoute>[
_goRouteDataBuild,
_goRouteDataBuildPage,
_goRouteDataBuildPageWithState,
_goRouteDataRedirect,
];
void main() {
@ -65,7 +67,6 @@ void main() {
));
expect(find.byKey(const Key('build')), findsOneWidget);
expect(find.byKey(const Key('buildPage')), findsNothing);
expect(find.byKey(const Key('buildPageWithState')), findsNothing);
},
);
@ -83,12 +84,11 @@ void main() {
));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsOneWidget);
expect(find.byKey(const Key('buildPageWithState')), findsNothing);
},
);
testWidgets(
'It should build the page from the overridden buildPageWithState method',
'It should build the page from the overridden buildPage method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/build-page-with-state',
@ -101,7 +101,39 @@ void main() {
));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsNothing);
expect(find.byKey(const Key('buildPageWithState')), findsOneWidget);
},
);
testWidgets(
'It should redirect using the overridden redirect method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/redirect',
routes: _routes,
);
await tester.pumpWidget(MaterialApp.router(
routeInformationProvider: goRouter.routeInformationProvider,
routeInformationParser: goRouter.routeInformationParser,
routerDelegate: goRouter.routerDelegate,
));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsOneWidget);
},
);
testWidgets(
'It should redirect using the overridden redirect method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/redirect-with-state',
routes: _routes,
);
await tester.pumpWidget(MaterialApp.router(
routeInformationProvider: goRouter.routeInformationProvider,
routeInformationParser: goRouter.routeInformationParser,
routerDelegate: goRouter.routerDelegate,
));
expect(find.byKey(const Key('build')), findsNothing);
expect(find.byKey(const Key('buildPage')), findsNothing);
},
);
}