Unverified Commit 3b4ac4d5 authored by chunhtai's avatar chunhtai Committed by GitHub

Implement url support for RouteInformation and didPushRouteInformation (#119968)

Related https://github.com/flutter/flutter/issues/100624

The goal is to make sure the engine can send a location string in either the existing format or a complete uri string to the framework, and the framework will still work as usual.
parent 034adb66
......@@ -80,14 +80,21 @@ abstract final class SystemNavigator {
///
/// The `replace` flag defaults to false.
static Future<void> routeInformationUpdated({
required String location,
@Deprecated(
'Pass Uri.parse(location) to uri parameter instead. '
'This feature was deprecated after v3.8.0-3.0.pre.'
)
String? location,
Uri? uri,
Object? state,
bool replace = false,
}) {
assert((location != null) != (uri != null), 'One of uri or location must be provided, but not both.');
uri ??= Uri.parse(location!);
return SystemChannels.navigation.invokeMethod<void>(
'routeInformationUpdated',
<String, dynamic>{
'location': location,
'uri': uri.toString(),
'state': state,
'replace': replace,
},
......
......@@ -1362,7 +1362,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
if (widget.routeInformationProvider == null && widget.routeInformationParser != null) {
_defaultRouteInformationProvider ??= PlatformRouteInformationProvider(
initialRouteInformation: RouteInformation(
location: _initialRouteName,
uri: Uri.parse(_initialRouteName),
),
);
} else {
......@@ -1484,7 +1484,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
}
@override
Future<bool> didPushRoute(String route) async {
Future<bool> didPushRouteInformation(RouteInformation routeInformation) async {
assert(mounted);
// The route name provider should handle the push route if we uses a
// router.
......@@ -1496,7 +1496,16 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
if (navigator == null) {
return false;
}
navigator.pushNamed(route);
final Uri uri = routeInformation.uri;
navigator.pushNamed(
Uri.decodeComponent(
Uri(
path: uri.path.isEmpty ? '/' : uri.path,
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
fragment: uri.fragment.isEmpty ? null : uri.fragment,
).toString(),
),
);
return true;
}
......
......@@ -72,6 +72,10 @@ abstract mixin class WidgetsBindingObserver {
///
/// This method exposes the `pushRoute` notification from
/// [SystemChannels.navigation].
@Deprecated(
'Use didPushRouteInformation instead. '
'This feature was deprecated after v3.8.0-14.0.pre.'
)
Future<bool> didPushRoute(String route) => Future<bool>.value(false);
/// Called when the host tells the application to push a new
......@@ -85,9 +89,20 @@ abstract mixin class WidgetsBindingObserver {
/// [SystemChannels.navigation].
///
/// The default implementation is to call the [didPushRoute] directly with the
/// [RouteInformation.location].
/// string constructed from [RouteInformation.uri]'s path and query parameters.
// TODO(chunhtai): remove the default implementation once `didPushRoute` is
// removed.
Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
return didPushRoute(routeInformation.location!);
final Uri uri = routeInformation.uri;
return didPushRoute(
Uri.decodeComponent(
Uri(
path: uri.path.isEmpty ? '/' : uri.path,
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
fragment: uri.fragment.isEmpty ? null : uri.fragment,
).toString(),
),
);
}
/// Called when the application's dimensions change. For example,
......@@ -672,23 +687,21 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
@protected
@mustCallSuper
Future<void> handlePushRoute(String route) async {
final RouteInformation routeInformation = RouteInformation(uri: Uri.parse(route));
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
if (await observer.didPushRoute(route)) {
if (await observer.didPushRouteInformation(routeInformation)) {
return;
}
}
}
Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
final RouteInformation routeInformation = RouteInformation(
uri: Uri.parse(routeArguments['location'] as String),
state: routeArguments['state'] as Object?,
);
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
if (
await observer.didPushRouteInformation(
RouteInformation(
location: routeArguments['location'] as String,
state: routeArguments['state'] as Object?,
),
)
) {
if (await observer.didPushRouteInformation(routeInformation)) {
return;
}
}
......
......@@ -4050,7 +4050,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
);
final String? routeName = lastEntry?.route.settings.name;
if (routeName != null && routeName != _lastAnnouncedRouteName) {
SystemNavigator.routeInformationUpdated(location: routeName);
SystemNavigator.routeInformationUpdated(uri: Uri.parse(routeName));
_lastAnnouncedRouteName = routeName;
}
}
......
......@@ -41,18 +41,57 @@ import 'restoration_properties.dart';
class RouteInformation {
/// Creates a route information object.
///
/// The arguments may be null.
const RouteInformation({this.location, this.state});
/// Either location or uri must not be null.
const RouteInformation({
@Deprecated(
'Pass Uri.parse(location) to uri parameter instead. '
'This feature was deprecated after v3.8.0-3.0.pre.'
)
String? location,
Uri? uri,
this.state,
}) : _location = location,
_uri = uri,
assert((location != null) != (uri != null));
/// The location of the application.
///
/// The string is usually in the format of multiple string identifiers with
/// slashes in between. ex: `/`, `/path`, `/path/to/the/app`.
@Deprecated(
'Use uri instead. '
'This feature was deprecated after v3.8.0-3.0.pre.'
)
String get location {
if (_location != null) {
return _location!;
}
return Uri.decodeComponent(
Uri(
path: uri.path.isEmpty ? '/' : uri.path,
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
fragment: uri.fragment.isEmpty ? null : uri.fragment,
).toString(),
);
}
final String? _location;
/// The uri location of the application.
///
/// It is equivalent to the URL in a web application.
final String? location;
/// The host and scheme will not be empty if this object is created from a
/// deep link request. They represents the website that redirect the deep
/// link.
///
/// In web platform, the host and scheme are always empty.
Uri get uri {
if (_uri != null){
return _uri!;
}
return Uri.parse(_location!);
}
final Uri? _uri;
/// The state of the application in the [location].
/// The state of the application in the [uri].
///
/// The app can have different states even in the same location. For example,
/// the text inside a [TextField] or the scroll position in a [ScrollView].
......@@ -61,11 +100,11 @@ class RouteInformation {
/// On the web, this information is stored in the browser history when the
/// [Router] reports this route information back to the web engine
/// through the [PlatformRouteInformationProvider]. The information
/// is then passed back, along with the [location], when the user
/// is then passed back, along with the [uri], when the user
/// clicks the back or forward buttons.
///
/// This information is also serialized and persisted alongside the
/// [location] for state restoration purposes. During state restoration,
/// [uri] for state restoration purposes. During state restoration,
/// the information is made available again to the [Router] so it can restore
/// its configuration to the previous state.
///
......@@ -252,7 +291,7 @@ class RouterConfig<T> {
///
/// 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]
/// [RouteInformation.uri] has not changed by calling the [Router.navigate]
/// method with a callback that performs the state change. This causes [Router]
/// to call the [RouteInformationProvider.routerReportsNewRouteInformation] with
/// [RouteInformationReportingType.navigate], and thus causes
......@@ -471,7 +510,7 @@ class Router<T> extends StatefulWidget {
///
/// The web application relies on the [Router] to report new route information
/// in order to create browser history entry. The [Router] will only report
/// them if it detects the [RouteInformation.location] changes. Use this
/// them if it detects the [RouteInformation.uri] changes. Use this
/// method if you want the [Router] to report the route information even if
/// the location does not change. This can be useful when you want to
/// support the browser backward and forward button without changing the URL.
......@@ -502,7 +541,7 @@ class Router<T> extends StatefulWidget {
///
/// The web application relies on the [Router] to report new route information
/// in order to create browser history entry. The [Router] will report them
/// automatically if it detects the [RouteInformation.location] changes.
/// automatically if it detects the [RouteInformation.uri] changes.
///
/// Creating a new route history entry makes users feel they have visited a
/// new page, and the browser back button brings them back to previous history
......@@ -1432,10 +1471,10 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
final bool replace =
type == RouteInformationReportingType.neglect ||
(type == RouteInformationReportingType.none &&
_valueInEngine.location == routeInformation.location);
_valueInEngine.uri == routeInformation.uri);
SystemNavigator.selectMultiEntryHistory();
SystemNavigator.routeInformationUpdated(
location: routeInformation.location!,
uri: routeInformation.uri,
state: routeInformation.state,
replace: replace,
);
......@@ -1447,7 +1486,7 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
RouteInformation get value => _value;
RouteInformation _value;
RouteInformation _valueInEngine = RouteInformation(location: WidgetsBinding.instance.platformDispatcher.defaultRouteName);
RouteInformation _valueInEngine = RouteInformation(uri: Uri.parse(WidgetsBinding.instance.platformDispatcher.defaultRouteName));
void _platformReportsNewRouteInformation(RouteInformation routeInformation) {
if (_value == routeInformation) {
......@@ -1492,13 +1531,6 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
_platformReportsNewRouteInformation(routeInformation);
return true;
}
@override
Future<bool> didPushRoute(String route) async {
assert(hasListeners);
_platformReportsNewRouteInformation(RouteInformation(location: route));
return true;
}
}
/// A mixin that wires [RouterDelegate.popRoute] to the [Navigator] it builds.
......@@ -1542,11 +1574,15 @@ class _RestorableRouteInformation extends RestorableValue<RouteInformation?> {
}
assert(data is List<Object?> && data.length == 2);
final List<Object?> castedData = data as List<Object?>;
return RouteInformation(location: castedData.first as String?, state: castedData.last);
final String? uri = castedData.first as String?;
if (uri == null) {
return null;
}
return RouteInformation(uri: Uri.parse(uri), state: castedData.last);
}
@override
Object? toPrimitives() {
return value == null ? null : <Object?>[value!.location, value!.state];
return value == null ? null : <Object?>[value!.uri.toString(), value!.state];
}
}
......@@ -148,17 +148,17 @@ void main() {
testWidgets('CupertinoApp.router works', (WidgetTester tester) async {
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -180,16 +180,16 @@ void main() {
testWidgets('CupertinoApp.router route information parser is optional', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
await tester.pumpWidget(CupertinoApp.router(
routerDelegate: delegate,
));
......@@ -205,19 +205,19 @@ void main() {
testWidgets('CupertinoApp.router throw if route information provider is provided but no route information parser', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
await tester.pumpWidget(CupertinoApp.router(
......@@ -230,16 +230,16 @@ void main() {
testWidgets('CupertinoApp.router throw if route configuration is provided along with other delegate', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(routerDelegate: delegate);
await tester.pumpWidget(CupertinoApp.router(
routerDelegate: delegate,
......@@ -251,18 +251,18 @@ void main() {
testWidgets('CupertinoApp.router router config works', (WidgetTester tester) async {
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
),
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -532,7 +532,7 @@ class SimpleNavigatorRouterDelegate extends RouterDelegate<RouteInformation> wit
child: Text('base'),
),
CupertinoPage<void>(
key: ValueKey<String?>(routeInformation.location),
key: ValueKey<String?>(routeInformation.uri.toString()),
child: builder(context, routeInformation),
),
],
......
......@@ -1085,17 +1085,17 @@ void main() {
testWidgets('MaterialApp.router works', (WidgetTester tester) async {
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -1117,16 +1117,16 @@ void main() {
testWidgets('MaterialApp.router route information parser is optional', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
await tester.pumpWidget(MaterialApp.router(
routerDelegate: delegate,
));
......@@ -1142,19 +1142,19 @@ void main() {
testWidgets('MaterialApp.router throw if route information provider is provided but no route information parser', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
await tester.pumpWidget(MaterialApp.router(
......@@ -1167,16 +1167,16 @@ void main() {
testWidgets('MaterialApp.router throw if route configuration is provided along with other delegate', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(routerDelegate: delegate);
await tester.pumpWidget(MaterialApp.router(
routerDelegate: delegate,
......@@ -1188,18 +1188,18 @@ void main() {
testWidgets('MaterialApp.router router config works', (WidgetTester tester) async {
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
),
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -1601,7 +1601,7 @@ class SimpleNavigatorRouterDelegate extends RouterDelegate<RouteInformation> wit
child: Text('base'),
),
MaterialPage<void>(
key: ValueKey<String>(routeInformation.location!),
key: ValueKey<String>(routeInformation.uri.toString()),
child: builder(context, routeInformation),
),
],
......
......@@ -45,15 +45,15 @@ void main() {
]);
await verify(() => SystemNavigator.routeInformationUpdated(location: 'a'), <Object>[
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'a', 'state': null, 'replace': false }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'a', 'state': null, 'replace': false }),
]);
await verify(() => SystemNavigator.routeInformationUpdated(location: 'a', state: true), <Object>[
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'a', 'state': true, 'replace': false }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'a', 'state': true, 'replace': false }),
]);
await verify(() => SystemNavigator.routeInformationUpdated(location: 'a', state: true, replace: true), <Object>[
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'a', 'state': true, 'replace': true }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'a', 'state': true, 'replace': true }),
]);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.navigation, null);
......
......@@ -275,17 +275,17 @@ void main() {
testWidgets('WidgetsApp.router works', (WidgetTester tester) async {
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -308,16 +308,16 @@ void main() {
testWidgets('WidgetsApp.router route information parser is optional', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
await tester.pumpWidget(WidgetsApp.router(
routerDelegate: delegate,
color: const Color(0xFF123456),
......@@ -334,19 +334,19 @@ void main() {
testWidgets('WidgetsApp.router throw if route information provider is provided but no route information parser', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
await expectLater(() async {
......@@ -361,16 +361,16 @@ void main() {
testWidgets('WidgetsApp.router throw if route configuration is provided along with other delegate', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
);
delegate.routeInformation = const RouteInformation(location: 'initial');
delegate.routeInformation = RouteInformation(uri: Uri.parse('initial'));
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(routerDelegate: delegate);
await expectLater(() async {
await tester.pumpWidget(WidgetsApp.router(
......@@ -384,18 +384,18 @@ void main() {
testWidgets('WidgetsApp.router router config works', (WidgetTester tester) async {
final RouterConfig<RouteInformation> routerConfig = RouterConfig<RouteInformation>(
routeInformationProvider: PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
),
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -418,7 +418,7 @@ void main() {
testWidgets('WidgetsApp.router has correct default', (WidgetTester tester) async {
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
onPopPage: (Route<Object?> route, Object? result, SimpleNavigatorRouterDelegate delegate) => true,
);
......@@ -769,7 +769,7 @@ class SimpleNavigatorRouterDelegate extends RouterDelegate<RouteInformation> wit
child: Text('base'),
),
MaterialPage<void>(
key: ValueKey<String>(routeInformation.location!),
key: ValueKey<String>(routeInformation.uri.toString()),
child: builder(context, routeInformation),
),
],
......
......@@ -111,6 +111,38 @@ void main() {
WidgetsBinding.instance.removeObserver(observer);
});
testWidgets('didPushRouteInformation calls didPushRoute correctly when handling url', (WidgetTester tester) async {
final PushRouteObserver observer = PushRouteObserver();
WidgetsBinding.instance.addObserver(observer);
// A url without any path.
Map<String, dynamic> testRouteInformation = const <String, dynamic>{
'location': 'http://hostname',
'state': 'state',
'restorationData': <dynamic, dynamic>{'test': 'config'},
};
ByteData message = const JSONMethodCodec().encodeMethodCall(
MethodCall('pushRouteInformation', testRouteInformation),
);
await ServicesBinding.instance.defaultBinaryMessenger
.handlePlatformMessage('flutter/navigation', message, (_) {});
expect(observer.pushedRoute, '/');
// A complex url.
testRouteInformation = const <String, dynamic>{
'location': 'http://hostname/abc?def=123&def=456#789',
'state': 'state',
'restorationData': <dynamic, dynamic>{'test': 'config'},
};
message = const JSONMethodCodec().encodeMethodCall(
MethodCall('pushRouteInformation', testRouteInformation),
);
await ServicesBinding.instance.defaultBinaryMessenger
.handlePlatformMessage('flutter/navigation', message, (_) {});
expect(observer.pushedRoute, '/abc?def=123&def=456#789');
WidgetsBinding.instance.removeObserver(observer);
});
testWidgets('didPushRouteInformation callback', (WidgetTester tester) async {
final PushRouteInformationObserver observer = PushRouteInformationObserver();
WidgetsBinding.instance.addObserver(observer);
......@@ -123,7 +155,25 @@ void main() {
const MethodCall('pushRouteInformation', testRouteInformation),
);
await tester.binding.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
expect(observer.pushedRouteInformation.location, 'testRouteName');
expect(observer.pushedRouteInformation.uri.toString(), 'testRouteName');
expect(observer.pushedRouteInformation.state, 'state');
WidgetsBinding.instance.removeObserver(observer);
});
testWidgets('didPushRouteInformation callback can handle url', (WidgetTester tester) async {
final PushRouteInformationObserver observer = PushRouteInformationObserver();
WidgetsBinding.instance.addObserver(observer);
const Map<String, dynamic> testRouteInformation = <String, dynamic>{
'location': 'http://hostname/abc?def=123&def=456#789',
'state': 'state',
};
final ByteData message = const JSONMethodCodec().encodeMethodCall(
const MethodCall('pushRouteInformation', testRouteInformation),
);
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
expect(observer.pushedRouteInformation.location, '/abc?def=123&def=456#789');
expect(observer.pushedRouteInformation.uri.toString(), 'http://hostname/abc?def=123&def=456#789');
expect(observer.pushedRouteInformation.state, 'state');
WidgetsBinding.instance.removeObserver(observer);
});
......@@ -139,8 +189,9 @@ void main() {
final ByteData message = const JSONMethodCodec().encodeMethodCall(
const MethodCall('pushRouteInformation', testRouteInformation),
);
await tester.binding.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
expect(observer.pushedRouteInformation.location, 'testRouteName');
expect(observer.pushedRouteInformation.uri.toString(), 'testRouteName');
expect(observer.pushedRouteInformation.state, null);
WidgetsBinding.instance.removeObserver(observer);
});
......
......@@ -63,7 +63,7 @@ void main() {
isMethodCall('selectSingleEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/',
'uri': '/',
'state': null,
'replace': false,
},
......@@ -81,7 +81,7 @@ void main() {
isMethodCall(
'routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/A',
'uri': '/A',
'state': null,
'replace': false,
},
......@@ -99,7 +99,7 @@ void main() {
isMethodCall(
'routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/',
'uri': '/',
'state': null,
'replace': false,
},
......@@ -173,7 +173,7 @@ void main() {
isMethodCall('selectSingleEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/',
'uri': '/',
'state': null,
'replace': false,
},
......@@ -191,7 +191,7 @@ void main() {
isMethodCall(
'routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/A',
'uri': '/A',
'state': null,
'replace': false,
},
......@@ -209,7 +209,7 @@ void main() {
isMethodCall(
'routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/B',
'uri': '/B',
'state': null,
'replace': false,
},
......@@ -246,7 +246,7 @@ void main() {
isMethodCall('selectSingleEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated',
arguments: <String, dynamic>{
'location': '/home',
'uri': '/home',
'state': null,
'replace': false,
},
......@@ -269,14 +269,14 @@ void main() {
});
final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
final SimpleRouterDelegate delegate = SimpleRouterDelegate(
reportConfiguration: true,
builder: (BuildContext context, RouteInformation information) {
return Text(information.location!);
return Text(information.uri.toString());
},
);
......@@ -289,7 +289,7 @@ void main() {
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
'location': 'initial',
'uri': 'initial',
'state': null,
'replace': false,
}),
......@@ -298,8 +298,8 @@ void main() {
// Triggers a router rebuild and verify the route information is reported
// to the web engine.
delegate.routeInformation = const RouteInformation(
location: 'update',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('update'),
state: 'state',
);
await tester.pump();
......@@ -308,7 +308,7 @@ void main() {
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
'location': 'update',
'uri': 'update',
'state': 'state',
'replace': false,
}),
......
......@@ -51,7 +51,7 @@ void main() {
expect(delegate().newRoutePaths, <String>['/home']);
expect(delegate().restoredRoutePaths, isEmpty);
provider().value = const RouteInformation(location: '/foo');
provider().value = RouteInformation(uri: Uri(path: '/foo'));
await tester.pumpAndSettle();
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/home', '/foo']);
......@@ -64,7 +64,7 @@ void main() {
final TestRestorationData restorationData = await tester.getRestorationData();
provider().value = const RouteInformation(location: '/bar');
provider().value = RouteInformation(uri: Uri.parse('/bar'));
await tester.pumpAndSettle();
expect(find.text('Current config: /bar'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/bar']);
......@@ -80,12 +80,12 @@ void main() {
class _TestRouteInformationParser extends RouteInformationParser<String> {
@override
Future<String> parseRouteInformation(RouteInformation routeInformation) {
return SynchronousFuture<String>(routeInformation.location!);
return SynchronousFuture<String>(routeInformation.uri.toString());
}
@override
RouteInformation? restoreRouteInformation(String configuration) {
return RouteInformation(location: configuration);
return RouteInformation(uri: Uri.parse(configuration));
}
}
......@@ -130,7 +130,7 @@ class _TestRouterDelegate extends RouterDelegate<String> with ChangeNotifier {
class _TestRouteInformationProvider extends RouteInformationProvider with ChangeNotifier {
@override
RouteInformation get value => _value;
RouteInformation _value = const RouteInformation(location: '/home');
RouteInformation _value = RouteInformation(uri: Uri.parse('/home'));
set value(RouteInformation value) {
if (value == _value) {
return;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment