[go_router] #127016 preserved route name case when caching `_nameToPath` (#4196)
preserved route name case when caching `_nameToPath` by not using `toLowerCase()`
Related issue:
[#127016](https://github.com/flutter/flutter/issues/127016)
diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index 28e8c8d..49764fb 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 8.0.3
+
+- Makes namedLocation and route name related APIs case sensitive.
+
## 8.0.2
- Fixes a bug in `debugLogDiagnostics` to support StatefulShellRoute.
diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart
index bd10649..89fb540 100644
--- a/packages/go_router/lib/src/configuration.dart
+++ b/packages/go_router/lib/src/configuration.dart
@@ -221,9 +221,8 @@
'${queryParameters.isEmpty ? '' : ', queryParameters: $queryParameters'}');
return true;
}());
- final String keyName = name.toLowerCase();
- assert(_nameToPath.containsKey(keyName), 'unknown route name: $name');
- final String path = _nameToPath[keyName]!;
+ assert(_nameToPath.containsKey(name), 'unknown route name: $name');
+ final String path = _nameToPath[name]!;
assert(() {
// Check that all required params are present
final List<String> paramNames = <String>[];
@@ -564,7 +563,7 @@
final String fullPath = concatenatePaths(parentFullPath, route.path);
if (route.name != null) {
- final String name = route.name!.toLowerCase();
+ final String name = route.name!;
assert(
!_nameToPath.containsKey(name),
'duplication fullpaths for name '
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index f5cd923..a2f48a7 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -1,7 +1,7 @@
name: go_router
description: A declarative router for Flutter based on Navigation 2 supporting
deep linking, data-driven routes and more
-version: 8.0.2
+version: 8.0.3
repository: https://github.com/flutter/packages/tree/main/packages/go_router
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart
index b3b4666..ed258aa 100644
--- a/packages/go_router/test/go_router_test.dart
+++ b/packages/go_router/test/go_router_test.dart
@@ -1493,8 +1493,7 @@
}, throwsA(isAssertionError));
});
- testWidgets('match case insensitive w/ params',
- (WidgetTester tester) async {
+ testWidgets('cannot match case insensitive', (WidgetTester tester) async {
final List<GoRoute> routes = <GoRoute>[
GoRoute(
name: 'home',
@@ -1524,8 +1523,15 @@
];
final GoRouter router = await createRouter(routes, tester);
- router.goNamed('person',
- pathParameters: <String, String>{'fid': 'f2', 'pid': 'p1'});
+ expect(
+ () {
+ router.goNamed(
+ 'person',
+ pathParameters: <String, String>{'fid': 'f2', 'pid': 'p1'},
+ );
+ },
+ throwsAssertionError,
+ );
});
testWidgets('too few params', (WidgetTester tester) async {
diff --git a/packages/go_router/test/name_case_test.dart b/packages/go_router/test/name_case_test.dart
new file mode 100644
index 0000000..6e3f067
--- /dev/null
+++ b/packages/go_router/test/name_case_test.dart
@@ -0,0 +1,66 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router/go_router.dart';
+
+void main() {
+ testWidgets(
+ 'Route names are case sensitive',
+ (WidgetTester tester) async {
+ // config router with 2 routes with the same name but different case (Name, name)
+ final GoRouter router = GoRouter(
+ routes: <GoRoute>[
+ GoRoute(
+ path: '/',
+ name: 'Name',
+ builder: (_, __) => const ScreenA(),
+ ),
+ GoRoute(
+ path: '/path',
+ name: 'name',
+ builder: (_, __) => const ScreenB(),
+ ),
+ ],
+ );
+
+ // run MaterialApp, initial screen path is '/' -> ScreenA
+ await tester.pumpWidget(
+ MaterialApp.router(
+ routerConfig: router,
+ title: 'GoRouter Testcase',
+ ),
+ );
+
+ // go to ScreenB
+ router.goNamed('name');
+ await tester.pumpAndSettle();
+ expect(find.byType(ScreenB), findsOneWidget);
+
+ // go to ScreenA
+ router.goNamed('Name');
+ await tester.pumpAndSettle();
+ expect(find.byType(ScreenA), findsOneWidget);
+ },
+ );
+}
+
+class ScreenA extends StatelessWidget {
+ const ScreenA({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+
+class ScreenB extends StatelessWidget {
+ const ScreenB({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
diff --git a/packages/go_router/test/parser_test.dart b/packages/go_router/test/parser_test.dart
index ee4279b..253790f 100644
--- a/packages/go_router/test/parser_test.dart
+++ b/packages/go_router/test/parser_test.dart
@@ -118,11 +118,8 @@
);
expect(configuration.namedLocation('lowercase'), '/abc');
- expect(configuration.namedLocation('LOWERCASE'), '/abc');
expect(configuration.namedLocation('camelCase'), '/efg');
- expect(configuration.namedLocation('camelcase'), '/efg');
expect(configuration.namedLocation('snake_case'), '/hij');
- expect(configuration.namedLocation('SNAKE_CASE'), '/hij');
// With query parameters
expect(configuration.namedLocation('lowercase'), '/abc');