Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions packages/go_router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/lib/src/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
Expand Down
8 changes: 4 additions & 4 deletions packages/go_router/lib/src/misc/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand All @@ -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,
Expand Down
43 changes: 13 additions & 30 deletions packages/go_router/lib/src/route_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
///
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions packages/go_router/lib/src/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -249,7 +249,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
_routerDelegate.navigatorKey.currentContext!,
)
.then<void>((RouteMatchList matchList) {
routerDelegate.replace(matchList);
routerDelegate.pushReplacement(matchList);
});
}

Expand All @@ -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,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
10 changes: 5 additions & 5 deletions packages/go_router/test/delegate_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -181,7 +181,7 @@ void main() {
);
});

group('replaceNamed', () {
group('pushReplacementNamed', () {
testWidgets(
'It should replace the last match with the given one',
(WidgetTester tester) async {
Expand Down Expand Up @@ -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,
Expand Down
64 changes: 48 additions & 16 deletions packages/go_router/test/route_data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
// 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';

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(
Expand All @@ -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')),
);
}
Expand All @@ -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() {
Expand All @@ -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);
},
);

Expand All @@ -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',
Expand All @@ -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);
},
);
}