Skip to content

Commit

Permalink
[go_router] Reduces excessive rebuilds due to inherited look up. (#4227)
Browse files Browse the repository at this point in the history
  • Loading branch information
chunhtai committed Jun 23, 2023
1 parent f55d455 commit 6b70804
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 101 deletions.
9 changes: 9 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 9.0.0

- **BREAKING CHANGE**:
- Removes GoRouter.location and GoRouter.canPop. Use GoRouterState.of().location and
Navigator.of().canPop instead.
- GoRouter does not `extends` ChangeNotifier.
- [Migration guide](https://flutter.dev/go/go-router-v9-breaking-changes)
- Reduces excessive rebuilds due to inherited look up.

## 8.2.0

- Adds onException to GoRouter constructor.
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 9.0.0](https://flutter.dev/go/go-router-v9-breaking-changes).
- [Migrating to 8.0.0](https://flutter.dev/go/go-router-v8-breaking-changes).
- [Migrating to 7.0.0](https://flutter.dev/go/go-router-v7-breaking-changes).
- [Migrating to 6.0.0](https://flutter.dev/go/go-router-v6-breaking-changes)
Expand Down
8 changes: 8 additions & 0 deletions packages/go_router/lib/src/information_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ class GoRouteInformationProvider extends RouteInformationProvider
RouteInformation get value => _value;
RouteInformation _value;

@override
// TODO(chunhtai): remove this ignore once package minimum dart version is
// above 3.
// ignore: unnecessary_overrides
void notifyListeners() {
super.notifyListeners();
}

void _setValue(String location, Object state) {
final bool shouldNotify =
_value.location != location || _value.state != state;
Expand Down
7 changes: 5 additions & 2 deletions packages/go_router/lib/src/misc/inherited_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import '../router.dart';
///
/// Used for to find the current GoRouter in the widget tree. This is useful
/// when routing from anywhere in your app.
class InheritedGoRouter extends InheritedNotifier<GoRouter> {
class InheritedGoRouter extends InheritedWidget {
/// Default constructor for the inherited go router.
const InheritedGoRouter({
required super.child,
required this.goRouter,
super.key,
}) : super(notifier: goRouter);
});

/// The [GoRouter] that is made available to the widget tree.
final GoRouter goRouter;
Expand All @@ -27,4 +27,7 @@ class InheritedGoRouter extends InheritedNotifier<GoRouter> {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<GoRouter>('goRouter', goRouter));
}

@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
46 changes: 9 additions & 37 deletions packages/go_router/lib/src/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ typedef GoExceptionHandler = void Function(
/// {@category Deep linking}
/// {@category Error handling}
/// {@category Named routes}
class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
class GoRouter implements RouterConfig<RouteMatchList> {
/// Default constructor to configure a GoRouter with a routes builder
/// and an error page builder.
///
Expand Down Expand Up @@ -152,7 +152,6 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
builderWithNav: (BuildContext context, Widget child) =>
InheritedGoRouter(goRouter: this, child: child),
);
routerDelegate.addListener(_handleStateMayChange);

assert(() {
log.info('setting initial location $initialLocation');
Expand Down Expand Up @@ -296,34 +295,9 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
@override
late final GoRouteInformationParser routeInformationParser;

/// Gets the current location.
// TODO(chunhtai): deprecates this once go_router_builder is migrated to
// GoRouterState.of.
String get location => _location;
String _location = '/';

/// Returns `true` if there is at least two or more route can be pop.
bool canPop() => routerDelegate.canPop();

void _handleStateMayChange() {
final String newLocation;
if (routerDelegate.currentConfiguration.isNotEmpty &&
routerDelegate.currentConfiguration.matches.last
is ImperativeRouteMatch) {
newLocation = (routerDelegate.currentConfiguration.matches.last
as ImperativeRouteMatch)
.matches
.uri
.toString();
} else {
newLocation = routerDelegate.currentConfiguration.uri.toString();
}
if (_location != newLocation) {
_location = newLocation;
notifyListeners();
}
}

/// Get a location from route name and parameters.
/// This is useful for redirecting to a named location.
String namedLocation(
Expand Down Expand Up @@ -488,7 +462,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
/// of any GoRoute under it.
void pop<T extends Object?>([T? result]) {
assert(() {
log.info('popping $location');
log.info('popping ${routerDelegate.currentConfiguration.uri}');
return true;
}());
routerDelegate.pop<T>(result);
Expand All @@ -497,7 +471,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
/// Refresh the route.
void refresh() {
assert(() {
log.info('refreshing $location');
log.info('refreshing ${routerDelegate.currentConfiguration.uri}');
return true;
}());
routeInformationProvider.notifyListeners();
Expand All @@ -507,27 +481,25 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
///
/// This method throws when it is called during redirects.
static GoRouter of(BuildContext context) {
final InheritedGoRouter? inherited =
context.dependOnInheritedWidgetOfExactType<InheritedGoRouter>();
final GoRouter? inherited = maybeOf(context);
assert(inherited != null, 'No GoRouter found in context');
return inherited!.goRouter;
return inherited!;
}

/// The current GoRouter in the widget tree, if any.
///
/// This method returns null when it is called during redirects.
static GoRouter? maybeOf(BuildContext context) {
final InheritedGoRouter? inherited =
context.dependOnInheritedWidgetOfExactType<InheritedGoRouter>();
final InheritedGoRouter? inherited = context
.getElementForInheritedWidgetOfExactType<InheritedGoRouter>()
?.widget as InheritedGoRouter?;
return inherited?.goRouter;
}

@override
/// Disposes resource created by this object.
void dispose() {
routeInformationProvider.dispose();
routerDelegate.removeListener(_handleStateMayChange);
routerDelegate.dispose();
super.dispose();
}

String _effectiveInitialLocation(String? initialLocation) {
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: 8.2.0
version: 9.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
6 changes: 5 additions & 1 deletion packages/go_router/test/extension_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:go_router/src/match.dart';

void main() {
group('replaceNamed', () {
Expand Down Expand Up @@ -37,7 +38,10 @@ void main() {
final GoRouter router = await createGoRouter(tester);
await tester.tap(find.text('Settings'));
await tester.pumpAndSettle();
expect(router.location, '/page-0/settings?search=notification');
final ImperativeRouteMatch routeMatch = router
.routerDelegate.currentConfiguration.last as ImperativeRouteMatch;
expect(routeMatch.matches.uri.toString(),
'/page-0/settings?search=notification');
});
});
}
Expand Down
Loading

0 comments on commit 6b70804

Please sign in to comment.