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 {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
if (
await observer.didPushRouteInformation(
RouteInformation(
location: routeArguments['location'] as String,
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)) {
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.
///
/// 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.
///
/// It is equivalent to the URL in a web application.
final String? location;
/// 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;
......
......@@ -12,8 +12,8 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Simple router basic functionality - synchronized', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
await tester.pumpWidget(buildBoilerPlate(
Router<RouteInformation>(
......@@ -21,15 +21,15 @@ void main() {
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
),
),
));
expect(find.text('initial'), findsOneWidget);
provider.value = const RouteInformation(
location: 'update',
provider.value = RouteInformation(
uri: Uri.parse('update'),
);
await tester.pump();
expect(find.text('initial'), findsNothing);
......@@ -38,8 +38,8 @@ void main() {
testWidgets('Simple router basic functionality - asynchronized', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final SimpleAsyncRouteInformationParser parser = SimpleAsyncRouteInformationParser();
final SimpleAsyncRouterDelegate delegate = SimpleAsyncRouterDelegate(
......@@ -47,7 +47,7 @@ void main() {
if (information == null) {
return const Text('waiting');
}
return Text(information.location!);
return Text(information.uri.toString());
},
);
await tester.runAsync(() async {
......@@ -66,8 +66,8 @@ void main() {
await tester.pump();
expect(find.text('initial'), findsOneWidget);
provider.value = const RouteInformation(
location: 'update',
provider.value = RouteInformation(
uri: Uri.parse('update'),
);
await tester.pump();
// Future has not yet completed.
......@@ -82,8 +82,8 @@ void main() {
testWidgets('Interrupts route parsing should not crash', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final CompleterRouteInformationParser parser = CompleterRouteInformationParser();
final SimpleAsyncRouterDelegate delegate = SimpleAsyncRouterDelegate(
......@@ -91,7 +91,7 @@ void main() {
if (information == null) {
return const Text('waiting');
}
return Text(information.location!);
return Text(information.uri.toString());
},
);
await tester.runAsync(() async {
......@@ -108,8 +108,8 @@ void main() {
final Completer<void> firstTransactionCompleter = parser.completer;
// Start a new parsing transaction before the previous one complete.
provider.value = const RouteInformation(
location: 'update',
provider.value = RouteInformation(
uri: Uri.parse('update'),
);
await tester.pump();
expect(find.text('waiting'), findsOneWidget);
......@@ -148,8 +148,8 @@ void main() {
testWidgets('Simple router can handle pop route', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher dispatcher = RootBackButtonDispatcher();
......@@ -159,11 +159,11 @@ void main() {
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped',
provider.value = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
},
......@@ -187,8 +187,8 @@ void main() {
testWidgets('Router throw when passing routeInformationProvider without routeInformationParser', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
expect(
() {
......@@ -196,7 +196,7 @@ void main() {
routeInformationProvider: provider,
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
),
);
......@@ -211,17 +211,17 @@ void main() {
testWidgets('PopNavigatorRouterDelegateMixin works', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher dispatcher = RootBackButtonDispatcher();
final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
onPopPage: (Route<void> route, void result) {
provider.value = const RouteInformation(
location: 'popped',
provider.value = RouteInformation(
uri: Uri.parse('popped'),
);
return route.didPop(result);
},
......@@ -264,8 +264,8 @@ void main() {
testWidgets('Nested routers back button dispatcher works', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
await tester.pumpWidget(buildBoilerPlate(
......@@ -282,11 +282,11 @@ void main() {
backButtonDispatcher: innerDispatcher,
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? innerInformation) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped inner',
provider.value = RouteInformation(
uri: Uri.parse('popped inner'),
);
return SynchronousFuture<bool>(true);
},
......@@ -294,8 +294,8 @@ void main() {
);
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
},
......@@ -314,8 +314,8 @@ void main() {
testWidgets('Nested router back button dispatcher works for multiple children', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
final BackButtonDispatcher innerDispatcher1 = ChildBackButtonDispatcher(outerDispatcher);
......@@ -330,7 +330,7 @@ void main() {
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
Router<RouteInformation>(
backButtonDispatcher: innerDispatcher1,
routerDelegate: SimpleRouterDelegate(
......@@ -338,8 +338,8 @@ void main() {
return Container();
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped inner1',
provider.value = RouteInformation(
uri: Uri.parse('popped inner1'),
);
return SynchronousFuture<bool>(true);
},
......@@ -352,8 +352,8 @@ void main() {
return Container();
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped inner2',
provider.value = RouteInformation(
uri: Uri.parse('popped inner2'),
);
return SynchronousFuture<bool>(true);
},
......@@ -363,8 +363,8 @@ void main() {
);
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
},
......@@ -522,19 +522,19 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
final SimpleRouterDelegate delegate = SimpleRouterDelegate(
reportConfiguration: true,
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
);
delegate.onPopRoute = () {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
};
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
await tester.pumpWidget(buildBoilerPlate(
......@@ -546,29 +546,29 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
),
));
expect(find.text('initial'), findsOneWidget);
expect(reportedRouteInformation!.location, 'initial');
expect(reportedRouteInformation!.uri.toString(), 'initial');
expect(reportedType, RouteInformationReportingType.none);
reportedRouteInformation = null;
reportedType = null;
delegate.routeInformation = const RouteInformation(
location: 'update',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('update'),
);
await tester.pump();
expect(find.text('initial'), findsNothing);
expect(find.text('update'), findsOneWidget);
expect(reportedRouteInformation!.location, 'update');
expect(reportedRouteInformation!.uri.toString(), 'update');
expect(reportedType, RouteInformationReportingType.none);
// The router should report as non navigation event if only state changes.
reportedRouteInformation = null;
reportedType = null;
delegate.routeInformation = const RouteInformation(
location: 'update',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('update'),
state: 'another state',
);
await tester.pump();
expect(find.text('update'), findsOneWidget);
expect(reportedRouteInformation!.location, 'update');
expect(reportedRouteInformation!.uri.toString(), 'update');
expect(reportedRouteInformation!.state, 'another state');
expect(reportedType, RouteInformationReportingType.none);
......@@ -579,7 +579,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
expect(result, isTrue);
await tester.pump();
expect(find.text('popped'), findsOneWidget);
expect(reportedRouteInformation!.location, 'popped');
expect(reportedRouteInformation!.uri.toString(), 'popped');
expect(reportedType, RouteInformationReportingType.none);
});
......@@ -597,13 +597,13 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
reportedType = type;
},
);
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final SimpleRouterDelegate delegate = SimpleRouterDelegate(reportConfiguration: true);
delegate.builder = (BuildContext context, RouteInformation? information) {
return ElevatedButton(
child: Text(information!.location!),
child: Text(Uri.decodeComponent(information!.uri.toString())),
onPressed: () {
if (isNavigating) {
Router.navigate(context, () {
......@@ -632,20 +632,20 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
),
));
expect(find.text('initial'), findsOneWidget);
expect(reportedRouteInformation!.location, 'initial');
expect(reportedRouteInformation!.uri.toString(), 'initial');
expect(reportedType, RouteInformationReportingType.none);
reportedType = null;
reportedRouteInformation = null;
nextRouteInformation = const RouteInformation(
location: 'update',
nextRouteInformation = RouteInformation(
uri: Uri.parse('update'),
);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.text('initial'), findsNothing);
expect(find.text('update'), findsOneWidget);
expect(reportedType, RouteInformationReportingType.neglect);
expect(reportedRouteInformation!.location, 'update');
expect(reportedRouteInformation!.uri.toString(), 'update');
reportedType = null;
reportedRouteInformation = null;
......@@ -656,7 +656,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(reportedType, RouteInformationReportingType.navigate);
expect(reportedRouteInformation!.location, 'update');
expect(reportedRouteInformation!.uri.toString(), 'update');
reportedType = null;
reportedRouteInformation = null;
});
......@@ -673,13 +673,13 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
reportingType = type;
},
);
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final SimpleRouterDelegate delegate = SimpleRouterDelegate(reportConfiguration: true);
delegate.builder = (BuildContext context, RouteInformation? information) {
return ElevatedButton(
child: Text(information!.location!),
child: Text(Uri.decodeComponent(information!.uri.toString())),
onPressed: () {
Router.neglect(context, () {
if (delegate.routeInformation != nextRouteInformation) {
......@@ -700,19 +700,19 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
),
));
expect(find.text('initial'), findsOneWidget);
expect(updatedRouteInformation!.location, 'initial');
expect(updatedRouteInformation!.uri.toString(), 'initial');
expect(reportingType, RouteInformationReportingType.none);
updatedRouteInformation = null;
reportingType = null;
nextRouteInformation = const RouteInformation(
location: 'update',
nextRouteInformation = RouteInformation(
uri: Uri.parse('update'),
);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.text('initial'), findsNothing);
expect(find.text('update'), findsOneWidget);
expect(updatedRouteInformation!.location, 'update');
expect(updatedRouteInformation!.uri.toString(), 'update');
expect(reportingType, RouteInformationReportingType.neglect);
});
......@@ -729,14 +729,14 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
reportingType = type;
},
);
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
state: 'state1',
);
final SimpleRouterDelegate delegate = SimpleRouterDelegate(reportConfiguration: true);
delegate.builder = (BuildContext context, RouteInformation? information) {
return ElevatedButton(
child: Text(information!.location!),
child: Text(Uri.decodeComponent(information!.uri.toString())),
onPressed: () {
delegate.routeInformation = nextRouteInformation;
},
......@@ -753,33 +753,33 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
),
));
expect(find.text('initial'), findsOneWidget);
expect(updatedRouteInformation!.location, 'initial');
expect(updatedRouteInformation!.uri.toString(), 'initial');
expect(reportingType, RouteInformationReportingType.none);
updatedRouteInformation = null;
reportingType = null;
nextRouteInformation = const RouteInformation(
location: 'initial',
nextRouteInformation = RouteInformation(
uri: Uri.parse('initial'),
state: 'state2',
);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(updatedRouteInformation!.location, 'initial');
expect(updatedRouteInformation!.uri.toString(), 'initial');
expect(updatedRouteInformation!.state, 'state2');
expect(reportingType, RouteInformationReportingType.none);
});
testWidgets('PlatformRouteInformationProvider works', (WidgetTester tester) async {
final RouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
final SimpleRouterDelegate delegate = SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
final List<Widget> children = <Widget>[];
if (information!.location != null) {
children.add(Text(information.location!));
if (information!.uri.toString().isNotEmpty) {
children.add(Text(information.uri.toString()));
}
if (information.state != null) {
children.add(Text(information.state.toString()));
......@@ -832,57 +832,57 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
}
);
final RouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: const RouteInformation(
location: 'initial',
initialRouteInformation: RouteInformation(
uri: Uri.parse('initial'),
),
);
log.clear();
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'a', state: true));
provider.routerReportsNewRouteInformation(RouteInformation(uri: Uri.parse('a'), state: true));
// 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 }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'a', 'state': true, 'replace': false }),
]);
log.clear();
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'a', state: false));
provider.routerReportsNewRouteInformation(RouteInformation(uri: Uri.parse('a'), state: false));
// 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 }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'a', 'state': false, 'replace': true }),
]);
log.clear();
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'b', state: false), type: RouteInformationReportingType.neglect);
provider.routerReportsNewRouteInformation(RouteInformation(uri: Uri.parse('b'), state: false), type: RouteInformationReportingType.neglect);
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'b', 'state': false, 'replace': true }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'b', 'state': false, 'replace': true }),
]);
log.clear();
provider.routerReportsNewRouteInformation(const RouteInformation(location: 'b', state: false), type: RouteInformationReportingType.navigate);
provider.routerReportsNewRouteInformation(RouteInformation(uri: Uri.parse('b'), state: false), type: RouteInformationReportingType.navigate);
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'location': 'b', 'state': false, 'replace': false }),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'b', 'state': false, 'replace': false }),
]);
});
testWidgets('RootBackButtonDispatcher works', (WidgetTester tester) async {
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
final RouteInformationProvider 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(Uri.decodeComponent(information!.uri.toString()));
},
);
delegate.onPopRoute = () {
delegate.routeInformation = const RouteInformation(
location: 'popped',
delegate.routeInformation = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
};
......@@ -904,8 +904,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('BackButtonListener takes priority over root back dispatcher', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
await tester.pumpWidget(buildBoilerPlate(
......@@ -918,12 +918,12 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'popped inner1',
provider.value = RouteInformation(
uri: Uri.parse('popped inner1'),
);
return SynchronousFuture<bool>(true);
},
......@@ -932,8 +932,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
},
......@@ -951,8 +951,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('BackButtonListener updates callback if it has been changed', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
final SimpleRouterDelegate routerDelegate = SimpleRouterDelegate()
......@@ -960,12 +960,12 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'first callback',
provider.value = RouteInformation(
uri: Uri.parse('first callback'),
);
return SynchronousFuture<bool>(true);
},
......@@ -974,8 +974,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
}
..onPopRoute = () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
};
......@@ -994,12 +994,12 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'second callback',
provider.value = RouteInformation(
uri: Uri.parse('second callback'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1008,8 +1008,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
}
..onPopRoute = () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
};
......@@ -1030,8 +1030,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('BackButtonListener clears callback if it is disposed', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
final SimpleRouterDelegate routerDelegate = SimpleRouterDelegate()
......@@ -1039,12 +1039,12 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'first callback',
provider.value = RouteInformation(
uri: Uri.parse('first callback'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1053,8 +1053,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
}
..onPopRoute = () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
};
......@@ -1073,13 +1073,13 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
],
);
}
..onPopRoute = () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
};
......@@ -1100,8 +1100,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('Nested backButtonListener should take priority', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
await tester.pumpWidget(buildBoilerPlate(
......@@ -1114,20 +1114,20 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'popped inner2',
provider.value = RouteInformation(
uri: Uri.parse('popped inner2'),
);
return SynchronousFuture<bool>(true);
},
),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'popped inner1',
provider.value = RouteInformation(
uri: Uri.parse('popped inner1'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1136,8 +1136,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1155,8 +1155,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('Nested backButtonListener that returns false should call next on the line', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
await tester.pumpWidget(buildBoilerPlate(
......@@ -1169,20 +1169,20 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
BackButtonListener(
child: BackButtonListener(
child: Container(),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'popped inner2',
provider.value = RouteInformation(
uri: Uri.parse('popped inner2'),
);
return SynchronousFuture<bool>(false);
},
),
onBackButtonPressed: () {
provider.value = const RouteInformation(
location: 'popped inner1',
provider.value = RouteInformation(
uri: Uri.parse('popped inner1'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1191,8 +1191,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1210,8 +1210,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('`didUpdateWidget` test', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
late StateSetter setState;
......@@ -1221,7 +1221,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
// Creates the sub-router.
return Column(
children: <Widget>[
Text(information!.location!),
Text(Uri.decodeComponent(information!.uri.toString())),
StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
......@@ -1229,7 +1229,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
child: Container(),
onBackButtonPressed: () {
provider.value = RouteInformation(
location: location,
uri: Uri.parse(location),
);
return SynchronousFuture<bool>(true);
},
......@@ -1240,8 +1240,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
);
}
..onPopRoute = () {
provider.value = const RouteInformation(
location: 'popped outer',
provider.value = RouteInformation(
uri: Uri.parse('popped outer'),
);
return SynchronousFuture<bool>(true);
};
......@@ -1270,35 +1270,35 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
final List<RouteInformation> reportedRouteInformation = <RouteInformation>[];
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(
onRouterReport: (RouteInformation info, RouteInformationReportingType type) => reportedRouteInformation.add(info),
)..value = const RouteInformation(location: '/home');
)..value = RouteInformation(uri: Uri.parse('/home'));
await tester.pumpWidget(buildBoilerPlate(
Router<RouteInformation>(
routeInformationProvider: provider,
routeInformationParser: RedirectingInformationParser(<String, RouteInformation>{
'/doesNotExist' : const RouteInformation(location: '/404'),
'/doesNotExist' : RouteInformation(uri: Uri.parse('/404')),
}),
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext _, RouteInformation? info) => Text('Current route: ${info?.location}'),
builder: (BuildContext _, RouteInformation? info) => Text('Current route: ${info?.uri}'),
reportConfiguration: true,
),
),
));
expect(find.text('Current route: /home'), findsOneWidget);
expect(reportedRouteInformation.single.location, '/home');
expect(reportedRouteInformation.single.uri.toString(), '/home');
provider.value = const RouteInformation(location: '/doesNotExist');
provider.value = RouteInformation(uri: Uri.parse('/doesNotExist'));
await tester.pump();
expect(find.text('Current route: /404'), findsOneWidget);
expect(reportedRouteInformation[1].location, '/404');
expect(reportedRouteInformation[1].uri.toString(), '/404');
});
testWidgets('RouterInformationParser can look up dependencies and reparse', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher dispatcher = RootBackButtonDispatcher();
int expectedMaxLines = 1;
......@@ -1308,15 +1308,15 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
routeInformationParser: CustomRouteInformationParser((RouteInformation information, BuildContext context) {
parserCalled = true;
final DefaultTextStyle style = DefaultTextStyle.of(context);
return RouteInformation(location: '${style.maxLines}');
return RouteInformation(uri: Uri.parse('${style.maxLines}'));
}),
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped',
provider.value = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1350,8 +1350,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('RouterInformationParser can look up dependencies without reparsing', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher dispatcher = RootBackButtonDispatcher();
const int expectedMaxLines = 1;
......@@ -1361,15 +1361,15 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
routeInformationParser: CustomRouteInformationParser((RouteInformation information, BuildContext context) {
parserCalled = true;
final DefaultTextStyle style = context.getInheritedWidgetOfExactType<DefaultTextStyle>()!;
return RouteInformation(location: '${style.maxLines}');
return RouteInformation(uri: Uri.parse('${style.maxLines}'));
}),
routerDelegate: SimpleRouterDelegate(
builder: (BuildContext context, RouteInformation? information) {
return Text(information!.location!);
return Text(Uri.decodeComponent(information!.uri.toString()));
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped',
provider.value = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1405,8 +1405,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('Looks up dependencies in RouterDelegate does not trigger re-parsing', (WidgetTester tester) async {
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
provider.value = const RouteInformation(
location: 'initial',
provider.value = RouteInformation(
uri: Uri.parse('initial'),
);
final BackButtonDispatcher dispatcher = RootBackButtonDispatcher();
int expectedMaxLines = 1;
......@@ -1423,8 +1423,8 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
return Text('${style.maxLines}');
},
onPopRoute: () {
provider.value = const RouteInformation(
location: 'popped',
provider.value = RouteInformation(
uri: Uri.parse('popped'),
);
return SynchronousFuture<bool>(true);
},
......@@ -1460,7 +1460,7 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
testWidgets('Router can initialize with RouterConfig', (WidgetTester tester) async {
const String expected = 'text';
final RouterConfig<RouteInformation> config = RouterConfig<RouteInformation>(
routeInformationProvider: SimpleRouteInformationProvider()..value = const RouteInformation(location: '/'),
routeInformationProvider: SimpleRouteInformationProvider()..value = RouteInformation(uri: Uri.parse('/')),
routeInformationParser: SimpleRouteInformationParser(),
routerDelegate: SimpleRouterDelegate(
builder: (_, __) => const Text(expected),
......@@ -1477,6 +1477,41 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
expect(find.text(expected), findsOneWidget);
});
group('RouteInformation uri api', () {
test('can produce correct uri from location', () async {
final RouteInformation info1 = RouteInformation(uri: Uri.parse('/a?abc=def&abc=jkl#mno'));
expect(info1.location, '/a?abc=def&abc=jkl#mno');
final Uri uri1 = info1.uri;
expect(uri1.scheme, '');
expect(uri1.host, '');
expect(uri1.path, '/a');
expect(uri1.fragment, 'mno');
expect(uri1.queryParametersAll.length, 1);
expect(uri1.queryParametersAll['abc']!.length, 2);
expect(uri1.queryParametersAll['abc']![0], 'def');
expect(uri1.queryParametersAll['abc']![1], 'jkl');
final RouteInformation info2 = RouteInformation(uri: Uri.parse('1'));
expect(info2.location, '1');
final Uri uri2 = info2.uri;
expect(uri2.scheme, '');
expect(uri2.host, '');
expect(uri2.path, '1');
expect(uri2.fragment, '');
expect(uri2.queryParametersAll.length, 0);
});
test('can produce correct location from uri', () async {
final RouteInformation info1 = RouteInformation(uri: Uri.parse('http://mydomain.com'));
expect(info1.uri.toString(), 'http://mydomain.com');
expect(info1.location, '/');
final RouteInformation info2 = RouteInformation(uri: Uri.parse('http://mydomain.com/abc?def=ghi&def=jkl#mno'));
expect(info2.uri.toString(), 'http://mydomain.com/abc?def=ghi&def=jkl#mno');
expect(info2.location, '/abc?def=ghi&def=jkl#mno');
});
});
}
Widget buildBoilerPlate(Widget child) {
......@@ -1605,7 +1640,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),
),
],
......@@ -1707,7 +1742,7 @@ class RedirectingInformationParser extends RouteInformationParser<RouteInformati
@override
Future<RouteInformation> parseRouteInformation(RouteInformation information) {
return SynchronousFuture<RouteInformation>(redirects[information.location] ?? information);
return SynchronousFuture<RouteInformation>(redirects[information.uri.toString()] ?? information);
}
@override
......
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