From ef59417168169ae19ffc90d8cf487da478480311 Mon Sep 17 00:00:00 2001 From: Sarbagya Dhaubanjar Date: Thu, 23 Mar 2023 10:15:59 +0545 Subject: [PATCH 1/5] :sparkles: added `onPushRoute` --- .../lib/src/information_provider.dart | 58 +++++++++++++++---- packages/go_router/lib/src/router.dart | 6 ++ .../test/information_provider_test.dart | 14 ++++- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/packages/go_router/lib/src/information_provider.dart b/packages/go_router/lib/src/information_provider.dart index 7d8ee4f2ab3..a41e5442587 100644 --- a/packages/go_router/lib/src/information_provider.dart +++ b/packages/go_router/lib/src/information_provider.dart @@ -2,23 +2,46 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart'; +import 'dart:async'; + import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +/// The decision on how to handle the route +/// when host tells the application to push a new one. +enum PushRouteDecision { + /// Delegate the route information to [WidgetsBindingObserver.didPushRoute]), + /// in registration order, until one returns true. + delegate, + + /// Prevent the route information from being addressed by [GoRouter]. + prevent, + + /// Delegate the route information to the [GoRouter]. + navigate, +} + +/// Signature for callbacks that report a pushed route information. +typedef PushRouteCallback = FutureOr Function( + RouteInformation routeInformation, +); + /// The [RouteInformationProvider] created by go_router. class GoRouteInformationProvider extends RouteInformationProvider with WidgetsBindingObserver, ChangeNotifier { /// Creates a [GoRouteInformationProvider]. GoRouteInformationProvider({ required RouteInformation initialRouteInformation, + required PushRouteCallback onPushRoute, Listenable? refreshListenable, }) : _refreshListenable = refreshListenable, + _onPushRoute = onPushRoute, _value = initialRouteInformation { _refreshListenable?.addListener(notifyListeners); } final Listenable? _refreshListenable; + final PushRouteCallback _onPushRoute; // ignore: unnecessary_non_null_assertion static WidgetsBinding get _binding => WidgetsBinding.instance; @@ -58,13 +81,22 @@ class GoRouteInformationProvider extends RouteInformationProvider RouteInformation _valueInEngine = RouteInformation(location: _binding.platformDispatcher.defaultRouteName); - void _platformReportsNewRouteInformation(RouteInformation routeInformation) { - if (_value == routeInformation) { - return; + Future _platformReportsNewRouteInformation( + RouteInformation routeInformation, + ) async { + switch (await _onPushRoute(routeInformation)) { + case PushRouteDecision.delegate: + return false; + case PushRouteDecision.prevent: + return true; + case PushRouteDecision.navigate: + if (_value != routeInformation) { + _value = routeInformation; + _valueInEngine = routeInformation; + notifyListeners(); + } + return true; } - _value = routeInformation; - _valueInEngine = routeInformation; - notifyListeners(); } @override @@ -93,16 +125,18 @@ class GoRouteInformationProvider extends RouteInformationProvider } @override - Future didPushRouteInformation(RouteInformation routeInformation) { + Future didPushRouteInformation( + RouteInformation routeInformation, + ) async { assert(hasListeners); - _platformReportsNewRouteInformation(routeInformation); - return SynchronousFuture(true); + return _platformReportsNewRouteInformation(routeInformation); } @override Future didPushRoute(String route) { assert(hasListeners); - _platformReportsNewRouteInformation(RouteInformation(location: route)); - return SynchronousFuture(true); + return _platformReportsNewRouteInformation( + RouteInformation(location: route), + ); } } diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index 29603720112..d8d08b226d3 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -64,6 +64,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { bool debugLogDiagnostics = false, GlobalKey? navigatorKey, String? restorationScopeId, + PushRouteCallback onPushRoute = _defaultPushRouteHandler, }) : backButtonDispatcher = RootBackButtonDispatcher(), assert( initialExtra == null || initialLocation != null, @@ -91,6 +92,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { location: _effectiveInitialLocation(initialLocation), state: initialExtra, ), + onPushRoute: onPushRoute, refreshListenable: refreshListenable, ); @@ -392,3 +394,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { } } } + +PushRouteDecision _defaultPushRouteHandler(RouteInformation routeInformation) { + return PushRouteDecision.navigate; +} diff --git a/packages/go_router/test/information_provider_test.dart b/packages/go_router/test/information_provider_test.dart index e6ce0945508..0f6d2ba68f1 100644 --- a/packages/go_router/test/information_provider_test.dart +++ b/packages/go_router/test/information_provider_test.dart @@ -9,12 +9,19 @@ import 'package:go_router/src/information_provider.dart'; const RouteInformation initialRoute = RouteInformation(location: '/'); const RouteInformation newRoute = RouteInformation(location: '/new'); +PushRouteDecision _defaultPushRouteHandler(RouteInformation routeInformation) { + return PushRouteDecision.navigate; +} + void main() { group('GoRouteInformationProvider', () { testWidgets('notifies its listeners when set by the app', (WidgetTester tester) async { late final GoRouteInformationProvider provider = - GoRouteInformationProvider(initialRouteInformation: initialRoute); + GoRouteInformationProvider( + initialRouteInformation: initialRoute, + onPushRoute: _defaultPushRouteHandler, + ); provider.addListener(expectAsync0(() {})); provider.value = newRoute; }); @@ -22,7 +29,10 @@ void main() { testWidgets('notifies its listeners when set by the platform', (WidgetTester tester) async { late final GoRouteInformationProvider provider = - GoRouteInformationProvider(initialRouteInformation: initialRoute); + GoRouteInformationProvider( + initialRouteInformation: initialRoute, + onPushRoute: _defaultPushRouteHandler, + ); provider.addListener(expectAsync0(() {})); provider.didPushRouteInformation(newRoute); }); From b98952f964ef21cee607dd99057d073091aa580b Mon Sep 17 00:00:00 2001 From: Sarbagya Dhaubanjar Date: Thu, 23 Mar 2023 10:24:25 +0545 Subject: [PATCH 2/5] :sparkles: exported `PushRouteDecision` --- packages/go_router/lib/go_router.dart | 1 + packages/go_router/lib/src/information_provider.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/go_router/lib/go_router.dart b/packages/go_router/lib/go_router.dart index bcf426fcc13..be3d28e6508 100644 --- a/packages/go_router/lib/go_router.dart +++ b/packages/go_router/lib/go_router.dart @@ -8,6 +8,7 @@ library go_router; export 'src/configuration.dart' show GoRoute, GoRouterState, RouteBase, ShellRoute; +export 'src/information_provider.dart' show PushRouteDecision; export 'src/misc/extensions.dart'; export 'src/misc/inherited_router.dart'; export 'src/pages/custom_transition_page.dart'; diff --git a/packages/go_router/lib/src/information_provider.dart b/packages/go_router/lib/src/information_provider.dart index a41e5442587..c649e2f7445 100644 --- a/packages/go_router/lib/src/information_provider.dart +++ b/packages/go_router/lib/src/information_provider.dart @@ -10,7 +10,7 @@ import 'package:flutter/widgets.dart'; /// The decision on how to handle the route /// when host tells the application to push a new one. enum PushRouteDecision { - /// Delegate the route information to [WidgetsBindingObserver.didPushRoute]), + /// Delegate the route information to [WidgetsBindingObserver.didPushRoute], /// in registration order, until one returns true. delegate, From aff2e9d3826def35a998fdd6a1fd08688c32f9fd Mon Sep 17 00:00:00 2001 From: Sarbagya Dhaubanjar Date: Thu, 23 Mar 2023 10:57:02 +0545 Subject: [PATCH 3/5] :white_check_mark: added tests for route information provider --- .../lib/src/information_provider.dart | 12 +++---- .../test/information_provider_test.dart | 36 ++++++++++++++++--- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/go_router/lib/src/information_provider.dart b/packages/go_router/lib/src/information_provider.dart index c649e2f7445..9192eeea16b 100644 --- a/packages/go_router/lib/src/information_provider.dart +++ b/packages/go_router/lib/src/information_provider.dart @@ -90,6 +90,7 @@ class GoRouteInformationProvider extends RouteInformationProvider case PushRouteDecision.prevent: return true; case PushRouteDecision.navigate: + assert(hasListeners); if (_value != routeInformation) { _value = routeInformation; _valueInEngine = routeInformation; @@ -125,18 +126,13 @@ class GoRouteInformationProvider extends RouteInformationProvider } @override - Future didPushRouteInformation( - RouteInformation routeInformation, - ) async { - assert(hasListeners); + Future didPushRouteInformation(RouteInformation routeInformation) { return _platformReportsNewRouteInformation(routeInformation); } @override Future didPushRoute(String route) { - assert(hasListeners); - return _platformReportsNewRouteInformation( - RouteInformation(location: route), - ); + final RouteInformation routeInformation = RouteInformation(location: route); + return _platformReportsNewRouteInformation(routeInformation); } } diff --git a/packages/go_router/test/information_provider_test.dart b/packages/go_router/test/information_provider_test.dart index 0f6d2ba68f1..85b8a83908f 100644 --- a/packages/go_router/test/information_provider_test.dart +++ b/packages/go_router/test/information_provider_test.dart @@ -17,8 +17,7 @@ void main() { group('GoRouteInformationProvider', () { testWidgets('notifies its listeners when set by the app', (WidgetTester tester) async { - late final GoRouteInformationProvider provider = - GoRouteInformationProvider( + final GoRouteInformationProvider provider = GoRouteInformationProvider( initialRouteInformation: initialRoute, onPushRoute: _defaultPushRouteHandler, ); @@ -28,13 +27,42 @@ void main() { testWidgets('notifies its listeners when set by the platform', (WidgetTester tester) async { - late final GoRouteInformationProvider provider = - GoRouteInformationProvider( + final GoRouteInformationProvider provider = GoRouteInformationProvider( initialRouteInformation: initialRoute, onPushRoute: _defaultPushRouteHandler, ); provider.addListener(expectAsync0(() {})); provider.didPushRouteInformation(newRoute); }); + + group('[push route decision]', () { + test('didPushRoute is false for "delegate"', () async { + final GoRouteInformationProvider provider = GoRouteInformationProvider( + initialRouteInformation: initialRoute, + onPushRoute: (_) => PushRouteDecision.delegate, + ); + expect(await provider.didPushRoute('/new'), isFalse); + expect(await provider.didPushRouteInformation(newRoute), isFalse); + }); + + test('didPushRoute is true for "prevent"', () async { + final GoRouteInformationProvider provider = GoRouteInformationProvider( + initialRouteInformation: initialRoute, + onPushRoute: (_) => PushRouteDecision.prevent, + ); + expect(await provider.didPushRoute('/new'), isTrue); + expect(await provider.didPushRouteInformation(newRoute), isTrue); + }); + + test('didPushRoute is true for "navigate"', () async { + final GoRouteInformationProvider provider = GoRouteInformationProvider( + initialRouteInformation: initialRoute, + onPushRoute: (_) => PushRouteDecision.navigate, + ); + provider.addListener(expectAsync0(() {})); + expect(await provider.didPushRoute('/new'), isTrue); + expect(await provider.didPushRouteInformation(newRoute), isTrue); + }); + }); }); } From e55e4d7c4865b446e1ba4c93080c008a7e8b1f7c Mon Sep 17 00:00:00 2001 From: Sarbagya Dhaubanjar Date: Thu, 23 Mar 2023 11:43:42 +0545 Subject: [PATCH 4/5] :white_check_mark: added tests for push route decision --- .../lib/src/information_provider.dart | 10 +- packages/go_router/lib/src/router.dart | 6 +- packages/go_router/test/go_router_test.dart | 112 ++++++++++++++++++ .../test/information_provider_test.dart | 6 - packages/go_router/test/test_helpers.dart | 3 + 5 files changed, 123 insertions(+), 14 deletions(-) diff --git a/packages/go_router/lib/src/information_provider.dart b/packages/go_router/lib/src/information_provider.dart index 9192eeea16b..96800d11ffa 100644 --- a/packages/go_router/lib/src/information_provider.dart +++ b/packages/go_router/lib/src/information_provider.dart @@ -32,7 +32,7 @@ class GoRouteInformationProvider extends RouteInformationProvider /// Creates a [GoRouteInformationProvider]. GoRouteInformationProvider({ required RouteInformation initialRouteInformation, - required PushRouteCallback onPushRoute, + PushRouteCallback? onPushRoute, Listenable? refreshListenable, }) : _refreshListenable = refreshListenable, _onPushRoute = onPushRoute, @@ -41,7 +41,7 @@ class GoRouteInformationProvider extends RouteInformationProvider } final Listenable? _refreshListenable; - final PushRouteCallback _onPushRoute; + final PushRouteCallback? _onPushRoute; // ignore: unnecessary_non_null_assertion static WidgetsBinding get _binding => WidgetsBinding.instance; @@ -84,7 +84,11 @@ class GoRouteInformationProvider extends RouteInformationProvider Future _platformReportsNewRouteInformation( RouteInformation routeInformation, ) async { - switch (await _onPushRoute(routeInformation)) { + final PushRouteDecision decision = + await _onPushRoute?.call(routeInformation) ?? + PushRouteDecision.navigate; + + switch (decision) { case PushRouteDecision.delegate: return false; case PushRouteDecision.prevent: diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index d8d08b226d3..d38d139d438 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -64,7 +64,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { bool debugLogDiagnostics = false, GlobalKey? navigatorKey, String? restorationScopeId, - PushRouteCallback onPushRoute = _defaultPushRouteHandler, + PushRouteCallback? onPushRoute, }) : backButtonDispatcher = RootBackButtonDispatcher(), assert( initialExtra == null || initialLocation != null, @@ -394,7 +394,3 @@ class GoRouter extends ChangeNotifier implements RouterConfig { } } } - -PushRouteDecision _defaultPushRouteHandler(RouteInformation routeInformation) { - return PushRouteDecision.navigate; -} diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 17335136bd7..adc797e5b69 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -3380,6 +3380,118 @@ void main() { }, ); }); + + group('push route decision', () { + testWidgets( + 'defaults to "navigate"', + (WidgetTester tester) async { + final List routes = [ + GoRoute( + path: '/', + builder: (_, __) => const SizedBox(), + ), + GoRoute( + path: '/dummy', + builder: (_, __) => const _DidPushRouteWidget(), + ), + ]; + + final GoRouter router = await createRouter(routes, tester); + + sendPlatformUrl('/dummy'); + await tester.pumpAndSettle(); + + expect(router.location, '/dummy'); + expect(find.text('DidPushRoute: null'), findsOneWidget); + }, + ); + + testWidgets( + 'based on location', + (WidgetTester tester) async { + final List routes = [ + GoRoute( + path: '/', + builder: (_, __) => const _DidPushRouteWidget(), + ), + GoRoute( + path: '/dummy', + builder: (_, __) => const _DidPushRouteWidget(), + ), + ]; + + final GoRouter router = await createRouter( + routes, + tester, + onPushRoute: (RouteInformation routeInformation) { + final String? location = routeInformation.location; + + switch (location) { + case '/prevent': + return PushRouteDecision.prevent; + case '/delegate': + return PushRouteDecision.delegate; + } + + return PushRouteDecision.navigate; + }, + ); + + sendPlatformUrl('/dummy'); + await tester.pumpAndSettle(); + expect(router.location, '/dummy'); + expect(find.text('DidPushRoute: null'), findsOneWidget); + + sendPlatformUrl('/prevent'); + await tester.pumpAndSettle(); + expect(router.location, '/dummy'); + expect(find.text('DidPushRoute: null'), findsOneWidget); + + sendPlatformUrl('/delegate'); + await tester.pumpAndSettle(); + expect(router.location, '/dummy'); + expect(find.text('DidPushRoute: /delegate'), findsOneWidget); + }, + ); + }); +} + +class _DidPushRouteWidget extends StatefulWidget { + const _DidPushRouteWidget(); + + @override + State<_DidPushRouteWidget> createState() => _DidPushRouteWidgetState(); +} + +class _DidPushRouteWidgetState extends State<_DidPushRouteWidget> + with WidgetsBindingObserver { + String? _route; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Future didPushRoute(String route) { + if (mounted) { + _route = route; + setState(() {}); + } + return SynchronousFuture(true); + } + + @override + Widget build(BuildContext context) { + return Text('DidPushRoute: $_route'); + } } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/go_router/test/information_provider_test.dart b/packages/go_router/test/information_provider_test.dart index 85b8a83908f..7c98f00f509 100644 --- a/packages/go_router/test/information_provider_test.dart +++ b/packages/go_router/test/information_provider_test.dart @@ -9,17 +9,12 @@ import 'package:go_router/src/information_provider.dart'; const RouteInformation initialRoute = RouteInformation(location: '/'); const RouteInformation newRoute = RouteInformation(location: '/new'); -PushRouteDecision _defaultPushRouteHandler(RouteInformation routeInformation) { - return PushRouteDecision.navigate; -} - void main() { group('GoRouteInformationProvider', () { testWidgets('notifies its listeners when set by the app', (WidgetTester tester) async { final GoRouteInformationProvider provider = GoRouteInformationProvider( initialRouteInformation: initialRoute, - onPushRoute: _defaultPushRouteHandler, ); provider.addListener(expectAsync0(() {})); provider.value = newRoute; @@ -29,7 +24,6 @@ void main() { (WidgetTester tester) async { final GoRouteInformationProvider provider = GoRouteInformationProvider( initialRouteInformation: initialRoute, - onPushRoute: _defaultPushRouteHandler, ); provider.addListener(expectAsync0(() {})); provider.didPushRouteInformation(newRoute); diff --git a/packages/go_router/test/test_helpers.dart b/packages/go_router/test/test_helpers.dart index 04d12e7383e..e9faf81f324 100644 --- a/packages/go_router/test/test_helpers.dart +++ b/packages/go_router/test/test_helpers.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; +import 'package:go_router/src/information_provider.dart'; Future createGoRouter(WidgetTester tester) async { final GoRouter goRouter = GoRouter( @@ -145,6 +146,7 @@ Future createRouter( int redirectLimit = 5, GlobalKey? navigatorKey, GoRouterWidgetBuilder? errorBuilder, + PushRouteCallback? onPushRoute, }) async { final GoRouter goRouter = GoRouter( routes: routes, @@ -156,6 +158,7 @@ Future createRouter( (BuildContext context, GoRouterState state) => TestErrorScreen(state.error!), navigatorKey: navigatorKey, + onPushRoute: onPushRoute, ); await tester.pumpWidget( MaterialApp.router( From cee5f7bcf20d2c58a8b08d36967089ab387968b9 Mon Sep 17 00:00:00 2001 From: Sarbagya Dhaubanjar Date: Thu, 23 Mar 2023 11:46:53 +0545 Subject: [PATCH 5/5] :bookmark: bumped version to 6.4.2 --- packages/go_router/CHANGELOG.md | 3 +++ packages/go_router/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 87ec4b591ce..f118806243f 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,3 +1,6 @@ +## 6.4.2 +- Adds `onPushRoute` callback to **GoRouter** that can be used to intercept route pushes from the application host. + ## 6.4.1 - Adds `initialExtra` to **GoRouter** to pass extra data alongside `initialRoute`. diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index d4d4b29c454..5e2a4f1163f 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -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: 6.4.1 +version: 6.4.2 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