mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Makes PlatformInformationProvider aware of the browser default route … (#88122)
This commit is contained in:
parent
7864e64dbb
commit
89d1b9e4c5
|
@ -201,39 +201,36 @@ class RouteInformation {
|
|||
/// method.
|
||||
///
|
||||
/// If the location in the new route information is different from the
|
||||
/// current location, this is considered to be a navigation event, the router
|
||||
/// sends the new route information to the [routeInformationProvider]'s
|
||||
/// [RouteInformationProvider.routerReportsNewRouteInformation] method with
|
||||
/// `isNavigation` equals to true. That method as implemented in
|
||||
/// [PlatformRouteInformationProvider] uses
|
||||
/// [SystemNavigator.routeInformationUpdated] to notify the engine, and through
|
||||
/// that the browser, to create a history entry with the new url if the
|
||||
/// `isNavigation` is true.
|
||||
///
|
||||
/// If the location is the same as the current location but different state,
|
||||
/// the router still sends the new route information to the
|
||||
/// [routeInformationProvider]'s
|
||||
/// [RouteInformationProvider.routerReportsNewRouteInformation] but with
|
||||
/// `isNavigation` equals to false. This causes
|
||||
/// [PlatformRouteInformationProvider] replace current history entry instead
|
||||
/// of creating a new one.
|
||||
/// current location, this is considered to be a navigation event, the
|
||||
/// [PlatformRouteInformationProvider.routerReportsNewRouteInformation] method
|
||||
/// calls [SystemNavigator.routeInformationUpdated] with `replace = false` to
|
||||
/// notify the engine, and through that the browser, to create a history entry
|
||||
/// with the new url. Otherwise,
|
||||
/// [PlatformRouteInformationProvider.routerReportsNewRouteInformation] calls
|
||||
/// [SystemNavigator.routeInformationUpdated] with `replace = true` to update
|
||||
/// the current history entry with the latest [RouteInformation].
|
||||
///
|
||||
/// One can force the [Router] to report new route information as navigation
|
||||
/// event to the [routeInformationProvider] (and thus the browser) even if the
|
||||
/// [RouteInformation.location] has not changed by calling the [Router.navigate]
|
||||
/// method with a callback that performs the state change. This allows one to
|
||||
/// support the browser's back and forward buttons without changing the URL. For
|
||||
/// example, the scroll position of a scroll view may be saved in the
|
||||
/// [RouteInformation.state]. Using [Router.navigate] to update the scroll
|
||||
/// position causes the browser to create a new history entry with the
|
||||
/// method with a callback that performs the state change. This causes [Router]
|
||||
/// to call the [RouteInformationProvider.routerReportsNewRouteInformation] with
|
||||
/// [RouteInformationReportingType.navigate], and thus causes
|
||||
/// [PlatformRouteInformationProvider] to push a new history entry regardlessly.
|
||||
/// This allows one to support the browser's back and forward buttons without
|
||||
/// changing the URL. For example, the scroll position of a scroll view may be
|
||||
/// saved in the [RouteInformation.state]. Using [Router.navigate] to update the
|
||||
/// scroll position causes the browser to create a new history entry with the
|
||||
/// [RouteInformation.state] that stores this new scroll position. When the user
|
||||
/// clicks the back button, the app will go back to the previous scroll position
|
||||
/// without changing the URL in the location bar.
|
||||
///
|
||||
/// One can also force the [Router] to ignore a navigation event by making
|
||||
/// those changes during a callback passed to [Router.neglect]. The [Router]
|
||||
/// will not report the route information with `isNavigation` equals to false
|
||||
/// even if it detects location change as the result of running the callback.
|
||||
/// calls the [RouteInformationProvider.routerReportsNewRouteInformation] with
|
||||
/// [RouteInformationReportingType.neglect], and thus causes
|
||||
/// [PlatformRouteInformationProvider] to replace the current history entry
|
||||
/// regardlessly even if it detects location change.
|
||||
///
|
||||
/// To opt out of URL updates entirely, pass null for [routeInformationProvider]
|
||||
/// and [routeInformationParser]. This is not recommended in general, but may be
|
||||
|
@ -432,7 +429,7 @@ class Router<T> extends StatefulWidget {
|
|||
final _RouterScope scope = context
|
||||
.getElementForInheritedWidgetOfExactType<_RouterScope>()!
|
||||
.widget as _RouterScope;
|
||||
scope.routerState._setStateWithExplicitReportStatus(_IntentionToReportRouteInformation.must, callback);
|
||||
scope.routerState._setStateWithExplicitReportStatus(RouteInformationReportingType.navigate, callback);
|
||||
}
|
||||
|
||||
/// Forces the [Router] to run the [callback] without creating a new history
|
||||
|
@ -461,7 +458,7 @@ class Router<T> extends StatefulWidget {
|
|||
final _RouterScope scope = context
|
||||
.getElementForInheritedWidgetOfExactType<_RouterScope>()!
|
||||
.widget as _RouterScope;
|
||||
scope.routerState._setStateWithExplicitReportStatus(_IntentionToReportRouteInformation.ignore, callback);
|
||||
scope.routerState._setStateWithExplicitReportStatus(RouteInformationReportingType.neglect, callback);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -471,22 +468,33 @@ class Router<T> extends StatefulWidget {
|
|||
typedef _AsyncPassthrough<Q> = Future<Q> Function(Q);
|
||||
typedef _DelegateRouteSetter<T> = Future<void> Function(T);
|
||||
|
||||
// Whether to report the route information in this build cycle.
|
||||
enum _IntentionToReportRouteInformation {
|
||||
// We haven't receive any signal on whether to report.
|
||||
/// The [Router]'s intention when it reports a new [RouteInformation] to the
|
||||
/// [RouteInformationProvider].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [RouteInformationProvider.routerReportsNewRouteInformation]: which is
|
||||
/// called by the router when it has a new route information to report.
|
||||
enum RouteInformationReportingType {
|
||||
/// Router does not have a specific intention.
|
||||
///
|
||||
/// The router generates a new route information every time it detects route
|
||||
/// information may have change due to a rebuild. This is the default type if
|
||||
/// neither [Router.neglect] nor [Router.navigate] was used during the
|
||||
/// rebuild.
|
||||
none,
|
||||
// Report if route information changes.
|
||||
maybe,
|
||||
// Report regardless of route information changes.
|
||||
must,
|
||||
// Don't report regardless of route information changes.
|
||||
ignore,
|
||||
/// The accompanying [RouteInformation] were generated during a
|
||||
/// [Router.neglect] call.
|
||||
neglect,
|
||||
/// The accompanying [RouteInformation] were generated during a
|
||||
/// [Router.navigate] call.
|
||||
navigate,
|
||||
}
|
||||
|
||||
class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
||||
Object? _currentRouteInformationParserTransaction;
|
||||
Object? _currentRouterDelegateTransaction;
|
||||
_IntentionToReportRouteInformation _currentIntentionToReport = _IntentionToReportRouteInformation.none;
|
||||
RouteInformationReportingType? _currentIntentionToReport;
|
||||
final _RestorableRouteInformation _routeInformation = _RestorableRouteInformation();
|
||||
|
||||
@override
|
||||
|
@ -515,7 +523,7 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
|||
void _scheduleRouteInformationReportingTask() {
|
||||
if (_routeInformationReportingTaskScheduled || widget.routeInformationProvider == null)
|
||||
return;
|
||||
assert(_currentIntentionToReport != _IntentionToReportRouteInformation.none);
|
||||
assert(_currentIntentionToReport != null);
|
||||
_routeInformationReportingTaskScheduled = true;
|
||||
SchedulerBinding.instance!.addPostFrameCallback(_reportRouteInformation);
|
||||
}
|
||||
|
@ -525,31 +533,11 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
|||
_routeInformationReportingTaskScheduled = false;
|
||||
|
||||
if (_routeInformation.value != null) {
|
||||
final RouteInformation oldRouteInformation = widget.routeInformationProvider!.value;
|
||||
final RouteInformation currentRouteInformation = _routeInformation.value!;
|
||||
switch (_currentIntentionToReport) {
|
||||
case _IntentionToReportRouteInformation.none:
|
||||
assert(false, '_reportRouteInformation must not be called with _IntentionToReportRouteInformation.none');
|
||||
return;
|
||||
case _IntentionToReportRouteInformation.ignore:
|
||||
if (oldRouteInformation.location != currentRouteInformation.location ||
|
||||
oldRouteInformation.state != currentRouteInformation.state) {
|
||||
widget.routeInformationProvider!.routerReportsNewRouteInformation(currentRouteInformation, isNavigation: false);
|
||||
}
|
||||
break;
|
||||
case _IntentionToReportRouteInformation.maybe:
|
||||
if (oldRouteInformation.location != currentRouteInformation.location) {
|
||||
widget.routeInformationProvider!.routerReportsNewRouteInformation(currentRouteInformation);
|
||||
} else if (oldRouteInformation.state != currentRouteInformation.state) {
|
||||
widget.routeInformationProvider!.routerReportsNewRouteInformation(currentRouteInformation, isNavigation: false);
|
||||
}
|
||||
break;
|
||||
case _IntentionToReportRouteInformation.must:
|
||||
widget.routeInformationProvider!.routerReportsNewRouteInformation(currentRouteInformation);
|
||||
break;
|
||||
}
|
||||
assert(_currentIntentionToReport != null);
|
||||
widget.routeInformationProvider!.routerReportsNewRouteInformation(currentRouteInformation, type: _currentIntentionToReport!);
|
||||
}
|
||||
_currentIntentionToReport = _IntentionToReportRouteInformation.none;
|
||||
_currentIntentionToReport = RouteInformationReportingType.none;
|
||||
}
|
||||
|
||||
RouteInformation? _retrieveNewRouteInformation() {
|
||||
|
@ -560,13 +548,14 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
|||
}
|
||||
|
||||
void _setStateWithExplicitReportStatus(
|
||||
_IntentionToReportRouteInformation status,
|
||||
RouteInformationReportingType status,
|
||||
VoidCallback fn,
|
||||
) {
|
||||
assert(status != null);
|
||||
assert(status.index >= _IntentionToReportRouteInformation.must.index);
|
||||
assert(status.index >= RouteInformationReportingType.neglect.index);
|
||||
assert(() {
|
||||
if (_currentIntentionToReport.index >= _IntentionToReportRouteInformation.must.index &&
|
||||
if (_currentIntentionToReport != null &&
|
||||
_currentIntentionToReport != RouteInformationReportingType.none &&
|
||||
_currentIntentionToReport != status) {
|
||||
FlutterError.reportError(
|
||||
const FlutterErrorDetails(
|
||||
|
@ -587,9 +576,7 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
|||
|
||||
void _maybeNeedToReportRouteInformation() {
|
||||
_routeInformation.value = _retrieveNewRouteInformation();
|
||||
_currentIntentionToReport = _currentIntentionToReport != _IntentionToReportRouteInformation.none
|
||||
? _currentIntentionToReport
|
||||
: _IntentionToReportRouteInformation.maybe;
|
||||
_currentIntentionToReport ??= RouteInformationReportingType.none;
|
||||
_scheduleRouteInformationReportingTask();
|
||||
}
|
||||
|
||||
|
@ -1325,19 +1312,21 @@ abstract class RouteInformationProvider extends ValueListenable<RouteInformation
|
|||
/// other side effects. For example, the [PlatformRouteInformationProvider]
|
||||
/// overrides this method to report the route information back to the engine.
|
||||
///
|
||||
/// The [routeInformation] is the new route information after the navigation
|
||||
/// event.
|
||||
/// The `routeInformation` is the new route information generated by the
|
||||
/// Router rebuild, and it can be the same or different from the
|
||||
/// [value].
|
||||
///
|
||||
/// The [isNavigation] denotes whether the new route information is generated
|
||||
/// as a result of a navigation event. This information can be useful in a
|
||||
/// web application, for example, the [PlatformRouteInformationProvider] uses
|
||||
/// this flag to decide whether to create a browser history entry that enables
|
||||
/// browser backward and forward buttons.
|
||||
/// The `type` denotes the [Router]'s intention when it reports this
|
||||
/// `routeInformation`. It is useful when deciding how to update the internal
|
||||
/// state of [RouteInformationProvider] subclass with the `routeInformation`.
|
||||
/// For example, [PlatformRouteInformationProvider] uses this property to
|
||||
/// decide whether to push or replace the browser history entry with the new
|
||||
/// `routeInformation`.
|
||||
///
|
||||
/// For more information on how [Router] determines a navigation event, see
|
||||
/// the "URL updates for web applications" section in the [Router]
|
||||
/// documentation.
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {bool isNavigation = true}) {}
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {required RouteInformationReportingType type}) {}
|
||||
}
|
||||
|
||||
/// The route information provider that propagates the platform route information changes.
|
||||
|
@ -1360,24 +1349,32 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
|
|||
}) : _value = initialRouteInformation;
|
||||
|
||||
@override
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {bool isNavigation = true}) {
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {required RouteInformationReportingType type}) {
|
||||
final bool replace =
|
||||
type == RouteInformationReportingType.neglect ||
|
||||
(type == RouteInformationReportingType.none &&
|
||||
_valueInEngine.location == routeInformation.location);
|
||||
SystemNavigator.selectMultiEntryHistory();
|
||||
SystemNavigator.routeInformationUpdated(
|
||||
location: routeInformation.location!,
|
||||
state: routeInformation.state,
|
||||
replace: !isNavigation,
|
||||
replace: replace,
|
||||
);
|
||||
_value = routeInformation;
|
||||
_valueInEngine = routeInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
RouteInformation get value => _value;
|
||||
RouteInformation _value;
|
||||
|
||||
RouteInformation _valueInEngine = RouteInformation(location: WidgetsBinding.instance!.window.defaultRouteName);
|
||||
|
||||
void _platformReportsNewRouteInformation(RouteInformation routeInformation) {
|
||||
if (_value == routeInformation)
|
||||
return;
|
||||
_value = routeInformation;
|
||||
_valueInEngine = routeInformation;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
|
|
@ -286,6 +286,15 @@ void main() {
|
|||
routerDelegate: delegate,
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(log, <Object>[
|
||||
isMethodCall('selectMultiEntryHistory', arguments: null),
|
||||
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
|
||||
'location': 'initial',
|
||||
'state': null,
|
||||
'replace': false,
|
||||
}),
|
||||
]);
|
||||
log.clear();
|
||||
|
||||
// Triggers a router rebuild and verify the route information is reported
|
||||
// to the web engine.
|
||||
|
|
|
@ -477,14 +477,14 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
|
||||
testWidgets('router does report URL change correctly', (WidgetTester tester) async {
|
||||
RouteInformation? reportedRouteInformation;
|
||||
bool? reportedIsNavigation;
|
||||
RouteInformationReportingType? reportedType;
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation information, bool isNavigation) {
|
||||
onRouterReport: (RouteInformation information, RouteInformationReportingType type) {
|
||||
// Makes sure we only report once after manually cleaning up.
|
||||
expect(reportedRouteInformation, isNull);
|
||||
expect(reportedIsNavigation, isNull);
|
||||
expect(reportedType, isNull);
|
||||
reportedRouteInformation = information;
|
||||
reportedIsNavigation = isNavigation;
|
||||
reportedType = type;
|
||||
},
|
||||
);
|
||||
final SimpleRouterDelegate delegate = SimpleRouterDelegate(
|
||||
|
@ -514,7 +514,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
),
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(reportedRouteInformation, isNull);
|
||||
expect(reportedRouteInformation!.location, 'initial');
|
||||
expect(reportedType, RouteInformationReportingType.none);
|
||||
reportedRouteInformation = null;
|
||||
reportedType = null;
|
||||
delegate.routeInformation = const RouteInformation(
|
||||
location: 'update',
|
||||
);
|
||||
|
@ -522,11 +525,11 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
expect(find.text('initial'), findsNothing);
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
expect(reportedRouteInformation!.location, 'update');
|
||||
expect(reportedIsNavigation, isTrue);
|
||||
expect(reportedType, RouteInformationReportingType.none);
|
||||
|
||||
// The router should report as non navigation event if only state changes.
|
||||
reportedRouteInformation = null;
|
||||
reportedIsNavigation = null;
|
||||
reportedType = null;
|
||||
delegate.routeInformation = const RouteInformation(
|
||||
location: 'update',
|
||||
state: 'another state',
|
||||
|
@ -535,31 +538,31 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
expect(find.text('update'), findsOneWidget);
|
||||
expect(reportedRouteInformation!.location, 'update');
|
||||
expect(reportedRouteInformation!.state, 'another state');
|
||||
expect(reportedIsNavigation, isFalse);
|
||||
expect(reportedType, RouteInformationReportingType.none);
|
||||
|
||||
reportedRouteInformation = null;
|
||||
reportedIsNavigation = null;
|
||||
reportedType = null;
|
||||
bool result = false;
|
||||
result = await outerDispatcher.invokeCallback(SynchronousFuture<bool>(false));
|
||||
expect(result, isTrue);
|
||||
await tester.pump();
|
||||
expect(find.text('popped'), findsOneWidget);
|
||||
expect(reportedRouteInformation!.location, 'popped');
|
||||
expect(reportedIsNavigation, isTrue);
|
||||
expect(reportedType, RouteInformationReportingType.none);
|
||||
});
|
||||
|
||||
testWidgets('router can be forced to recognize or ignore navigating events', (WidgetTester tester) async {
|
||||
RouteInformation? reportedRouteInformation;
|
||||
bool? reportedIsNavigation;
|
||||
RouteInformationReportingType? reportedType;
|
||||
bool isNavigating = false;
|
||||
late RouteInformation nextRouteInformation;
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation information, bool isNavigation) {
|
||||
onRouterReport: (RouteInformation information, RouteInformationReportingType type) {
|
||||
// Makes sure we only report once after manually cleaning up.
|
||||
expect(reportedRouteInformation, isNull);
|
||||
expect(reportedIsNavigation, isNull);
|
||||
expect(reportedType, isNull);
|
||||
reportedRouteInformation = information;
|
||||
reportedIsNavigation = isNavigation;
|
||||
reportedType = type;
|
||||
},
|
||||
);
|
||||
provider.value = const RouteInformation(
|
||||
|
@ -595,7 +598,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
),
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(reportedRouteInformation, isNull);
|
||||
expect(reportedRouteInformation!.location, 'initial');
|
||||
expect(reportedType, RouteInformationReportingType.none);
|
||||
reportedType = null;
|
||||
reportedRouteInformation = null;
|
||||
|
||||
nextRouteInformation = const RouteInformation(
|
||||
location: 'update',
|
||||
|
@ -604,9 +610,9 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
await tester.pump();
|
||||
expect(find.text('initial'), findsNothing);
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
expect(reportedIsNavigation, isFalse);
|
||||
expect(reportedType, RouteInformationReportingType.neglect);
|
||||
expect(reportedRouteInformation!.location, 'update');
|
||||
reportedIsNavigation = null;
|
||||
reportedType = null;
|
||||
reportedRouteInformation = null;
|
||||
|
||||
isNavigating = true;
|
||||
|
@ -615,21 +621,22 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
// report a route information because isNavigating = true.
|
||||
await tester.tap(find.byType(ElevatedButton));
|
||||
await tester.pump();
|
||||
expect(reportedIsNavigation, isTrue);
|
||||
expect(reportedType, RouteInformationReportingType.navigate);
|
||||
expect(reportedRouteInformation!.location, 'update');
|
||||
reportedIsNavigation = null;
|
||||
reportedType = null;
|
||||
reportedRouteInformation = null;
|
||||
});
|
||||
|
||||
testWidgets('router ignore navigating events updates RouteInformationProvider', (WidgetTester tester) async {
|
||||
RouteInformation? updatedRouteInformation;
|
||||
late RouteInformation nextRouteInformation;
|
||||
RouteInformationReportingType? reportingType;
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation information, bool isNavigation) {
|
||||
// This should never be a navigation event.
|
||||
expect(isNavigation, false);
|
||||
onRouterReport: (RouteInformation information, RouteInformationReportingType type) {
|
||||
expect(reportingType, isNull);
|
||||
expect(updatedRouteInformation, isNull);
|
||||
updatedRouteInformation = information;
|
||||
reportingType = type;
|
||||
},
|
||||
);
|
||||
provider.value = const RouteInformation(
|
||||
|
@ -658,7 +665,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
),
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(updatedRouteInformation, isNull);
|
||||
expect(updatedRouteInformation!.location, 'initial');
|
||||
expect(reportingType, RouteInformationReportingType.none);
|
||||
updatedRouteInformation = null;
|
||||
reportingType = null;
|
||||
|
||||
nextRouteInformation = const RouteInformation(
|
||||
location: 'update',
|
||||
|
@ -668,17 +678,20 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
expect(find.text('initial'), findsNothing);
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
expect(updatedRouteInformation!.location, 'update');
|
||||
expect(reportingType, RouteInformationReportingType.neglect);
|
||||
});
|
||||
|
||||
testWidgets('state change without location changes updates RouteInformationProvider', (WidgetTester tester) async {
|
||||
RouteInformation? updatedRouteInformation;
|
||||
late RouteInformation nextRouteInformation;
|
||||
RouteInformationReportingType? reportingType;
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation information, bool isNavigation) {
|
||||
onRouterReport: (RouteInformation information, RouteInformationReportingType type) {
|
||||
// This should never be a navigation event.
|
||||
expect(isNavigation, false);
|
||||
expect(reportingType, isNull);
|
||||
expect(updatedRouteInformation, isNull);
|
||||
updatedRouteInformation = information;
|
||||
reportingType = type;
|
||||
},
|
||||
);
|
||||
provider.value = const RouteInformation(
|
||||
|
@ -705,7 +718,10 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
),
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(updatedRouteInformation, isNull);
|
||||
expect(updatedRouteInformation!.location, 'initial');
|
||||
expect(reportingType, RouteInformationReportingType.none);
|
||||
updatedRouteInformation = null;
|
||||
reportingType = null;
|
||||
|
||||
nextRouteInformation = const RouteInformation(
|
||||
location: 'initial',
|
||||
|
@ -715,45 +731,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
await tester.pump();
|
||||
expect(updatedRouteInformation!.location, 'initial');
|
||||
expect(updatedRouteInformation!.state, 'state2');
|
||||
});
|
||||
|
||||
testWidgets('router does not report when route information is up to date with route information provider', (WidgetTester tester) async {
|
||||
RouteInformation? reportedRouteInformation;
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation information, bool isNavigation) {
|
||||
reportedRouteInformation = information;
|
||||
},
|
||||
);
|
||||
provider.value = const RouteInformation(
|
||||
location: 'initial',
|
||||
);
|
||||
final SimpleRouterDelegate delegate = SimpleRouterDelegate(reportConfiguration: true);
|
||||
delegate.builder = (BuildContext context, RouteInformation? routeInformation) {
|
||||
return Text(routeInformation!.location!);
|
||||
};
|
||||
|
||||
await tester.pumpWidget(buildBoilerPlate(
|
||||
Router<RouteInformation>(
|
||||
routeInformationProvider: provider,
|
||||
routeInformationParser: SimpleRouteInformationParser(),
|
||||
routerDelegate: delegate,
|
||||
),
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(reportedRouteInformation, isNull);
|
||||
// This will cause the router to rebuild.
|
||||
provider.value = const RouteInformation(
|
||||
location: 'update',
|
||||
);
|
||||
// This will schedule the route reporting.
|
||||
delegate.notifyListeners();
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('initial'), findsNothing);
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
// The router should not report because the route name is already up to
|
||||
// date.
|
||||
expect(reportedRouteInformation, isNull);
|
||||
expect(reportingType, RouteInformationReportingType.none);
|
||||
});
|
||||
|
||||
testWidgets('PlatformRouteInformationProvider works', (WidgetTester tester) async {
|
||||
|
@ -823,18 +801,33 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
);
|
||||
|
||||
log.clear();
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'a', state: true));
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'a', state: true), type: RouteInformationReportingType.none);
|
||||
// Implicit reporting pushes new history entry if the location changes.
|
||||
expect(log, <Object>[
|
||||
isMethodCall('selectMultiEntryHistory', arguments: null),
|
||||
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'a', 'state': true, 'replace': false }),
|
||||
]);
|
||||
log.clear();
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'a', state: false), type: RouteInformationReportingType.none);
|
||||
// Since the location is the same, the provider sends replaces message.
|
||||
expect(log, <Object>[
|
||||
isMethodCall('selectMultiEntryHistory', arguments: null),
|
||||
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'a', 'state': false, 'replace': true }),
|
||||
]);
|
||||
|
||||
log.clear();
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'b', state: false), isNavigation: false);
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'b', state: false), type: RouteInformationReportingType.neglect);
|
||||
expect(log, <Object>[
|
||||
isMethodCall('selectMultiEntryHistory', arguments: null),
|
||||
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'b', 'state': false, 'replace': true }),
|
||||
]);
|
||||
|
||||
log.clear();
|
||||
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'b', state: false), type: RouteInformationReportingType.navigate);
|
||||
expect(log, <Object>[
|
||||
isMethodCall('selectMultiEntryHistory', arguments: null),
|
||||
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'b', 'state': false, 'replace': false }),
|
||||
]);
|
||||
});
|
||||
|
||||
testWidgets('RootBackButtonDispatcher works', (WidgetTester tester) async {
|
||||
|
@ -1239,7 +1232,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
testWidgets('Router reports location if it is different from location given by OS', (WidgetTester tester) async {
|
||||
final List<RouteInformation> reportedRouteInformation = <RouteInformation>[];
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
|
||||
onRouterReport: (RouteInformation info, bool isNavigation) => reportedRouteInformation.add(info),
|
||||
onRouterReport: (RouteInformation info, RouteInformationReportingType type) => reportedRouteInformation.add(info),
|
||||
)..value = const RouteInformation(location: '/home');
|
||||
|
||||
await tester.pumpWidget(buildBoilerPlate(
|
||||
|
@ -1256,13 +1249,13 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
|
|||
));
|
||||
|
||||
expect(find.text('Current route: /home'), findsOneWidget);
|
||||
expect(reportedRouteInformation, isEmpty);
|
||||
expect(reportedRouteInformation.single.location, '/home');
|
||||
|
||||
provider.value = const RouteInformation(location: '/doesNotExist');
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Current route: /404'), findsOneWidget);
|
||||
expect(reportedRouteInformation.single.location, '/404');
|
||||
expect(reportedRouteInformation[1].location, '/404');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1270,7 @@ Widget buildBoilerPlate(Widget child) {
|
|||
typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation?);
|
||||
typedef SimpleRouterDelegatePopRoute = Future<bool> Function();
|
||||
typedef SimpleNavigatorRouterDelegatePopPage<T> = bool Function(Route<T> route, T result);
|
||||
typedef RouterReportRouterInformation = void Function(RouteInformation, bool);
|
||||
typedef RouterReportRouterInformation = void Function(RouteInformation, RouteInformationReportingType);
|
||||
|
||||
class SimpleRouteInformationParser extends RouteInformationParser<RouteInformation> {
|
||||
SimpleRouteInformationParser();
|
||||
|
@ -1398,9 +1391,9 @@ class SimpleRouteInformationProvider extends RouteInformationProvider with Chang
|
|||
}
|
||||
|
||||
@override
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {bool isNavigation = true}) {
|
||||
void routerReportsNewRouteInformation(RouteInformation routeInformation, {required RouteInformationReportingType type}) {
|
||||
_value = routeInformation;
|
||||
onRouterReport?.call(routeInformation, isNavigation);
|
||||
onRouterReport?.call(routeInformation, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue