Skip to content

Commit 3b4ac4d

Browse files
authored
Implement url support for RouteInformation and didPushRouteInformation (#119968)
Related flutter/flutter#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.
1 parent 034adb6 commit 3b4ac4d

File tree

13 files changed

+466
-315
lines changed

13 files changed

+466
-315
lines changed

packages/flutter/lib/src/services/system_navigator.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,21 @@ abstract final class SystemNavigator {
8080
///
8181
/// The `replace` flag defaults to false.
8282
static Future<void> routeInformationUpdated({
83-
required String location,
83+
@Deprecated(
84+
'Pass Uri.parse(location) to uri parameter instead. '
85+
'This feature was deprecated after v3.8.0-3.0.pre.'
86+
)
87+
String? location,
88+
Uri? uri,
8489
Object? state,
8590
bool replace = false,
8691
}) {
92+
assert((location != null) != (uri != null), 'One of uri or location must be provided, but not both.');
93+
uri ??= Uri.parse(location!);
8794
return SystemChannels.navigation.invokeMethod<void>(
8895
'routeInformationUpdated',
8996
<String, dynamic>{
90-
'location': location,
97+
'uri': uri.toString(),
9198
'state': state,
9299
'replace': replace,
93100
},

packages/flutter/lib/src/widgets/app.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,7 +1362,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
13621362
if (widget.routeInformationProvider == null && widget.routeInformationParser != null) {
13631363
_defaultRouteInformationProvider ??= PlatformRouteInformationProvider(
13641364
initialRouteInformation: RouteInformation(
1365-
location: _initialRouteName,
1365+
uri: Uri.parse(_initialRouteName),
13661366
),
13671367
);
13681368
} else {
@@ -1484,7 +1484,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
14841484
}
14851485

14861486
@override
1487-
Future<bool> didPushRoute(String route) async {
1487+
Future<bool> didPushRouteInformation(RouteInformation routeInformation) async {
14881488
assert(mounted);
14891489
// The route name provider should handle the push route if we uses a
14901490
// router.
@@ -1496,7 +1496,16 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
14961496
if (navigator == null) {
14971497
return false;
14981498
}
1499-
navigator.pushNamed(route);
1499+
final Uri uri = routeInformation.uri;
1500+
navigator.pushNamed(
1501+
Uri.decodeComponent(
1502+
Uri(
1503+
path: uri.path.isEmpty ? '/' : uri.path,
1504+
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
1505+
fragment: uri.fragment.isEmpty ? null : uri.fragment,
1506+
).toString(),
1507+
),
1508+
);
15001509
return true;
15011510
}
15021511

packages/flutter/lib/src/widgets/binding.dart

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ abstract mixin class WidgetsBindingObserver {
7272
///
7373
/// This method exposes the `pushRoute` notification from
7474
/// [SystemChannels.navigation].
75+
@Deprecated(
76+
'Use didPushRouteInformation instead. '
77+
'This feature was deprecated after v3.8.0-14.0.pre.'
78+
)
7579
Future<bool> didPushRoute(String route) => Future<bool>.value(false);
7680

7781
/// Called when the host tells the application to push a new
@@ -85,9 +89,20 @@ abstract mixin class WidgetsBindingObserver {
8589
/// [SystemChannels.navigation].
8690
///
8791
/// The default implementation is to call the [didPushRoute] directly with the
88-
/// [RouteInformation.location].
92+
/// string constructed from [RouteInformation.uri]'s path and query parameters.
93+
// TODO(chunhtai): remove the default implementation once `didPushRoute` is
94+
// removed.
8995
Future<bool> didPushRouteInformation(RouteInformation routeInformation) {
90-
return didPushRoute(routeInformation.location!);
96+
final Uri uri = routeInformation.uri;
97+
return didPushRoute(
98+
Uri.decodeComponent(
99+
Uri(
100+
path: uri.path.isEmpty ? '/' : uri.path,
101+
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
102+
fragment: uri.fragment.isEmpty ? null : uri.fragment,
103+
).toString(),
104+
),
105+
);
91106
}
92107

93108
/// Called when the application's dimensions change. For example,
@@ -672,23 +687,21 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
672687
@protected
673688
@mustCallSuper
674689
Future<void> handlePushRoute(String route) async {
690+
final RouteInformation routeInformation = RouteInformation(uri: Uri.parse(route));
675691
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
676-
if (await observer.didPushRoute(route)) {
692+
if (await observer.didPushRouteInformation(routeInformation)) {
677693
return;
678694
}
679695
}
680696
}
681697

682698
Future<void> _handlePushRouteInformation(Map<dynamic, dynamic> routeArguments) async {
699+
final RouteInformation routeInformation = RouteInformation(
700+
uri: Uri.parse(routeArguments['location'] as String),
701+
state: routeArguments['state'] as Object?,
702+
);
683703
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.of(_observers)) {
684-
if (
685-
await observer.didPushRouteInformation(
686-
RouteInformation(
687-
location: routeArguments['location'] as String,
688-
state: routeArguments['state'] as Object?,
689-
),
690-
)
691-
) {
704+
if (await observer.didPushRouteInformation(routeInformation)) {
692705
return;
693706
}
694707
}

packages/flutter/lib/src/widgets/navigator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4050,7 +4050,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
40504050
);
40514051
final String? routeName = lastEntry?.route.settings.name;
40524052
if (routeName != null && routeName != _lastAnnouncedRouteName) {
4053-
SystemNavigator.routeInformationUpdated(location: routeName);
4053+
SystemNavigator.routeInformationUpdated(uri: Uri.parse(routeName));
40544054
_lastAnnouncedRouteName = routeName;
40554055
}
40564056
}

packages/flutter/lib/src/widgets/router.dart

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,57 @@ import 'restoration_properties.dart';
4141
class RouteInformation {
4242
/// Creates a route information object.
4343
///
44-
/// The arguments may be null.
45-
const RouteInformation({this.location, this.state});
44+
/// Either location or uri must not be null.
45+
const RouteInformation({
46+
@Deprecated(
47+
'Pass Uri.parse(location) to uri parameter instead. '
48+
'This feature was deprecated after v3.8.0-3.0.pre.'
49+
)
50+
String? location,
51+
Uri? uri,
52+
this.state,
53+
}) : _location = location,
54+
_uri = uri,
55+
assert((location != null) != (uri != null));
4656

4757
/// The location of the application.
4858
///
4959
/// The string is usually in the format of multiple string identifiers with
5060
/// slashes in between. ex: `/`, `/path`, `/path/to/the/app`.
61+
@Deprecated(
62+
'Use uri instead. '
63+
'This feature was deprecated after v3.8.0-3.0.pre.'
64+
)
65+
String get location {
66+
if (_location != null) {
67+
return _location!;
68+
}
69+
return Uri.decodeComponent(
70+
Uri(
71+
path: uri.path.isEmpty ? '/' : uri.path,
72+
queryParameters: uri.queryParametersAll.isEmpty ? null : uri.queryParametersAll,
73+
fragment: uri.fragment.isEmpty ? null : uri.fragment,
74+
).toString(),
75+
);
76+
}
77+
final String? _location;
78+
79+
/// The uri location of the application.
5180
///
52-
/// It is equivalent to the URL in a web application.
53-
final String? location;
81+
/// The host and scheme will not be empty if this object is created from a
82+
/// deep link request. They represents the website that redirect the deep
83+
/// link.
84+
///
85+
/// In web platform, the host and scheme are always empty.
86+
Uri get uri {
87+
if (_uri != null){
88+
return _uri!;
89+
}
90+
return Uri.parse(_location!);
91+
}
92+
final Uri? _uri;
5493

55-
/// The state of the application in the [location].
94+
/// The state of the application in the [uri].
5695
///
5796
/// The app can have different states even in the same location. For example,
5897
/// the text inside a [TextField] or the scroll position in a [ScrollView].
@@ -61,11 +100,11 @@ class RouteInformation {
61100
/// On the web, this information is stored in the browser history when the
62101
/// [Router] reports this route information back to the web engine
63102
/// through the [PlatformRouteInformationProvider]. The information
64-
/// is then passed back, along with the [location], when the user
103+
/// is then passed back, along with the [uri], when the user
65104
/// clicks the back or forward buttons.
66105
///
67106
/// This information is also serialized and persisted alongside the
68-
/// [location] for state restoration purposes. During state restoration,
107+
/// [uri] for state restoration purposes. During state restoration,
69108
/// the information is made available again to the [Router] so it can restore
70109
/// its configuration to the previous state.
71110
///
@@ -252,7 +291,7 @@ class RouterConfig<T> {
252291
///
253292
/// One can force the [Router] to report new route information as navigation
254293
/// event to the [routeInformationProvider] (and thus the browser) even if the
255-
/// [RouteInformation.location] has not changed by calling the [Router.navigate]
294+
/// [RouteInformation.uri] has not changed by calling the [Router.navigate]
256295
/// method with a callback that performs the state change. This causes [Router]
257296
/// to call the [RouteInformationProvider.routerReportsNewRouteInformation] with
258297
/// [RouteInformationReportingType.navigate], and thus causes
@@ -471,7 +510,7 @@ class Router<T> extends StatefulWidget {
471510
///
472511
/// The web application relies on the [Router] to report new route information
473512
/// in order to create browser history entry. The [Router] will only report
474-
/// them if it detects the [RouteInformation.location] changes. Use this
513+
/// them if it detects the [RouteInformation.uri] changes. Use this
475514
/// method if you want the [Router] to report the route information even if
476515
/// the location does not change. This can be useful when you want to
477516
/// support the browser backward and forward button without changing the URL.
@@ -502,7 +541,7 @@ class Router<T> extends StatefulWidget {
502541
///
503542
/// The web application relies on the [Router] to report new route information
504543
/// in order to create browser history entry. The [Router] will report them
505-
/// automatically if it detects the [RouteInformation.location] changes.
544+
/// automatically if it detects the [RouteInformation.uri] changes.
506545
///
507546
/// Creating a new route history entry makes users feel they have visited a
508547
/// new page, and the browser back button brings them back to previous history
@@ -1432,10 +1471,10 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
14321471
final bool replace =
14331472
type == RouteInformationReportingType.neglect ||
14341473
(type == RouteInformationReportingType.none &&
1435-
_valueInEngine.location == routeInformation.location);
1474+
_valueInEngine.uri == routeInformation.uri);
14361475
SystemNavigator.selectMultiEntryHistory();
14371476
SystemNavigator.routeInformationUpdated(
1438-
location: routeInformation.location!,
1477+
uri: routeInformation.uri,
14391478
state: routeInformation.state,
14401479
replace: replace,
14411480
);
@@ -1447,7 +1486,7 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
14471486
RouteInformation get value => _value;
14481487
RouteInformation _value;
14491488

1450-
RouteInformation _valueInEngine = RouteInformation(location: WidgetsBinding.instance.platformDispatcher.defaultRouteName);
1489+
RouteInformation _valueInEngine = RouteInformation(uri: Uri.parse(WidgetsBinding.instance.platformDispatcher.defaultRouteName));
14511490

14521491
void _platformReportsNewRouteInformation(RouteInformation routeInformation) {
14531492
if (_value == routeInformation) {
@@ -1492,13 +1531,6 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
14921531
_platformReportsNewRouteInformation(routeInformation);
14931532
return true;
14941533
}
1495-
1496-
@override
1497-
Future<bool> didPushRoute(String route) async {
1498-
assert(hasListeners);
1499-
_platformReportsNewRouteInformation(RouteInformation(location: route));
1500-
return true;
1501-
}
15021534
}
15031535

15041536
/// A mixin that wires [RouterDelegate.popRoute] to the [Navigator] it builds.
@@ -1542,11 +1574,15 @@ class _RestorableRouteInformation extends RestorableValue<RouteInformation?> {
15421574
}
15431575
assert(data is List<Object?> && data.length == 2);
15441576
final List<Object?> castedData = data as List<Object?>;
1545-
return RouteInformation(location: castedData.first as String?, state: castedData.last);
1577+
final String? uri = castedData.first as String?;
1578+
if (uri == null) {
1579+
return null;
1580+
}
1581+
return RouteInformation(uri: Uri.parse(uri), state: castedData.last);
15461582
}
15471583

15481584
@override
15491585
Object? toPrimitives() {
1550-
return value == null ? null : <Object?>[value!.location, value!.state];
1586+
return value == null ? null : <Object?>[value!.uri.toString(), value!.state];
15511587
}
15521588
}

0 commit comments

Comments
 (0)