Skip to content

Commit 315ebaf

Browse files
authored
PlatformRouteInformationProvider does not push new entry if query par… (flutter#130457)
�ameter is semanticsally the same The URI compare does not taking into account that query parameter may or may not be encoded, or the parameters' order can be different. However, they are all semantically the same. This pr makes PlatformRouteInformationProvider to take those into account when deciding whether it should push/replace the browser history entry.
1 parent 5870159 commit 315ebaf

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:async';
66
import 'dart:collection';
77

8+
import 'package:collection/collection.dart';
89
import 'package:flutter/foundation.dart';
910
import 'package:flutter/scheduler.dart';
1011
import 'package:flutter/services.dart';
@@ -1466,12 +1467,18 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
14661467
required RouteInformation initialRouteInformation,
14671468
}) : _value = initialRouteInformation;
14681469

1470+
static bool _equals(Uri a, Uri b) {
1471+
return a.path == b.path
1472+
&& a.fragment == b.fragment
1473+
&& const DeepCollectionEquality.unordered().equals(a.queryParametersAll, b.queryParametersAll);
1474+
}
1475+
14691476
@override
14701477
void routerReportsNewRouteInformation(RouteInformation routeInformation, {RouteInformationReportingType type = RouteInformationReportingType.none}) {
14711478
final bool replace =
14721479
type == RouteInformationReportingType.neglect ||
14731480
(type == RouteInformationReportingType.none &&
1474-
_valueInEngine.uri == routeInformation.uri);
1481+
_equals(_valueInEngine.uri, routeInformation.uri));
14751482
SystemNavigator.selectMultiEntryHistory();
14761483
SystemNavigator.routeInformationUpdated(
14771484
uri: routeInformation.uri,

packages/flutter/test/widgets/router_test.dart

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,76 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
867867
]);
868868
});
869869

870+
testWidgets('PlatformRouteInformationProvider does not push new entry if query parameters are semantically the same', (WidgetTester tester) async {
871+
final List<MethodCall> log = <MethodCall>[];
872+
TestDefaultBinaryMessengerBinding
873+
.instance.defaultBinaryMessenger
874+
.setMockMethodCallHandler(
875+
SystemChannels.navigation,
876+
(MethodCall methodCall) async {
877+
log.add(methodCall);
878+
return null;
879+
}
880+
);
881+
final RouteInformation initial = RouteInformation(
882+
uri: Uri.parse('initial?a=ws/abcd'),
883+
);
884+
final RouteInformationProvider provider = PlatformRouteInformationProvider(
885+
initialRouteInformation: initial
886+
);
887+
// Make sure engine is updated with initial route
888+
provider.routerReportsNewRouteInformation(initial);
889+
log.clear();
890+
891+
provider.routerReportsNewRouteInformation(
892+
RouteInformation(
893+
uri: Uri(
894+
path: 'initial',
895+
queryParameters: <String, String>{'a': 'ws/abcd'}, // This will be escaped.
896+
),
897+
),
898+
);
899+
expect(provider.value.uri.toString(), 'initial?a=ws%2Fabcd');
900+
// should use `replace: true`
901+
expect(log, <Object>[
902+
isMethodCall('selectMultiEntryHistory', arguments: null),
903+
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?a=ws%2Fabcd', 'state': null, 'replace': true }),
904+
]);
905+
log.clear();
906+
907+
provider.routerReportsNewRouteInformation(
908+
RouteInformation(uri: Uri.parse('initial?a=1&b=2')),
909+
);
910+
log.clear();
911+
912+
// Change query parameters order
913+
provider.routerReportsNewRouteInformation(
914+
RouteInformation(uri: Uri.parse('initial?b=2&a=1')),
915+
);
916+
// should use `replace: true`
917+
expect(log, <Object>[
918+
isMethodCall('selectMultiEntryHistory', arguments: null),
919+
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?b=2&a=1', 'state': null, 'replace': true }),
920+
]);
921+
log.clear();
922+
923+
provider.routerReportsNewRouteInformation(
924+
RouteInformation(uri: Uri.parse('initial?a=1&a=2')),
925+
);
926+
log.clear();
927+
928+
// Change query parameters order for same key
929+
provider.routerReportsNewRouteInformation(
930+
RouteInformation(uri: Uri.parse('initial?a=2&a=1')),
931+
);
932+
// should use `replace: true`
933+
expect(log, <Object>[
934+
isMethodCall('selectMultiEntryHistory', arguments: null),
935+
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?a=2&a=1', 'state': null, 'replace': true }),
936+
]);
937+
log.clear();
938+
});
939+
870940
testWidgets('RootBackButtonDispatcher works', (WidgetTester tester) async {
871941
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
872942
final RouteInformationProvider provider = PlatformRouteInformationProvider(

0 commit comments

Comments
 (0)