Skip to content

Commit dc57079

Browse files
[go_router_builder] Fixes trailing ? by comparing iterables (#8521)
Fixes flutter/flutter#127373
1 parent ef7e0d5 commit dc57079

File tree

9 files changed

+181
-23
lines changed

9 files changed

+181
-23
lines changed

packages/go_router_builder/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.7.5
2+
3+
- Fixes trailing `?` in the location when a go route has an empty default value.
4+
15
## 2.7.4
26

37
- Fixes an issue by removing unnecessary `const` in StatefulShellRouteData generation.

packages/go_router_builder/example/lib/all_types.g.dart

Lines changed: 34 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:go_router_builder_example/all_types.dart';
7+
8+
void main() {
9+
test('IterableRouteWithDefaultValues', () {
10+
expect(
11+
const IterableRouteWithDefaultValues().location,
12+
'/iterable-route-with-default-values',
13+
);
14+
15+
// Needs to not be a const to test
16+
// https://github.com/flutter/flutter/issues/127825.
17+
final Set<double> doubleSetField = <double>{};
18+
expect(
19+
IterableRouteWithDefaultValues(
20+
doubleSetField: doubleSetField,
21+
).location,
22+
'/iterable-route-with-default-values',
23+
);
24+
25+
expect(
26+
IterableRouteWithDefaultValues(
27+
doubleSetField: <double>{0.0, 1.0},
28+
).location,
29+
'/iterable-route-with-default-values?double-set-field=0.0&double-set-field=1.0',
30+
);
31+
32+
// Needs to not be a const to test
33+
// https://github.com/flutter/flutter/issues/127825.
34+
final Set<int> intSetField = <int>{0, 1};
35+
expect(
36+
IterableRouteWithDefaultValues(
37+
intSetField: intSetField,
38+
).location,
39+
'/iterable-route-with-default-values',
40+
);
41+
42+
expect(
43+
const IterableRouteWithDefaultValues(
44+
intSetField: <int>{0, 1, 2},
45+
).location,
46+
'/iterable-route-with-default-values?int-set-field=0&int-set-field=1&int-set-field=2',
47+
);
48+
});
49+
}

packages/go_router_builder/lib/src/route_config.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,9 @@ class GoRouteConfig extends RouteBaseConfig {
313313
if (param.type.isNullableType) {
314314
throw NullableDefaultValueError(param);
315315
}
316-
conditions.add('$parameterName != ${param.defaultValueCode!}');
316+
conditions.add(
317+
compareField(param, parameterName, param.defaultValueCode!),
318+
);
317319
} else if (param.type.isNullableType) {
318320
conditions.add('$parameterName != null');
319321
}
@@ -734,6 +736,7 @@ const Map<String, String> helperNames = <String, String>{
734736
convertMapValueHelperName: _convertMapValueHelper,
735737
boolConverterHelperName: _boolConverterHelper,
736738
enumExtensionHelperName: _enumConverterHelper,
739+
iterablesEqualHelperName: _iterableEqualsHelper,
737740
};
738741

739742
const String _convertMapValueHelper = '''
@@ -765,3 +768,18 @@ extension<T extends Enum> on Map<T, String> {
765768
T $enumExtensionHelperName(String value) =>
766769
entries.singleWhere((element) => element.value == value).key;
767770
}''';
771+
772+
const String _iterableEqualsHelper = '''
773+
bool $iterablesEqualHelperName<T>(Iterable<T>? iterable1, Iterable<T>? iterable2) {
774+
if (identical(iterable1, iterable2)) return true;
775+
if (iterable1 == null || iterable2 == null) return false;
776+
final iterator1 = iterable1.iterator;
777+
final iterator2 = iterable2.iterator;
778+
while (true) {
779+
final hasNext1 = iterator1.moveNext();
780+
final hasNext2 = iterator2.moveNext();
781+
if (hasNext1 != hasNext2) return false;
782+
if (!hasNext1) return true;
783+
if (iterator1.current != iterator2.current) return false;
784+
}
785+
}''';

packages/go_router_builder/lib/src/type_helpers.dart

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const String extraFieldName = r'$extra';
2828
/// Shared start of error message related to a likely code issue.
2929
const String likelyIssueMessage = 'Should never get here! File an issue!';
3030

31+
/// The name of the generated, private helper for comparing iterables.
32+
const String iterablesEqualHelperName = r'_$iterablesEqual';
33+
3134
const List<_TypeHelper> _helpers = <_TypeHelper>[
3235
_TypeHelperBigInt(),
3336
_TypeHelperBool(),
@@ -86,6 +89,22 @@ String encodeField(PropertyAccessorElement element) {
8689
);
8790
}
8891

92+
/// Returns the comparison of a parameter with its default value.
93+
///
94+
/// Otherwise, throws an [InvalidGenerationSourceError].
95+
String compareField(ParameterElement param, String value1, String value2) {
96+
for (final _TypeHelper helper in _helpers) {
97+
if (helper._matchesType(param.type)) {
98+
return helper._compare(param.name, param.defaultValueCode!);
99+
}
100+
}
101+
102+
throw InvalidGenerationSourceError(
103+
'The type `${param.type}` is not supported.',
104+
element: param,
105+
);
106+
}
107+
89108
/// Gets the name of the `const` map generated to help encode [Enum] types.
90109
String enumMapName(InterfaceType type) => '_\$${type.element.name}EnumMap';
91110

@@ -119,6 +138,8 @@ abstract class _TypeHelper {
119138
String _encode(String fieldName, DartType type);
120139

121140
bool _matchesType(DartType type);
141+
142+
String _compare(String value1, String value2) => '$value1 != $value2';
122143
}
123144

124145
class _TypeHelperBigInt extends _TypeHelperWithHelper {
@@ -252,9 +273,12 @@ class _TypeHelperUri extends _TypeHelperWithHelper {
252273
const TypeChecker.fromRuntime(Uri).isAssignableFromType(type);
253274
}
254275

255-
class _TypeHelperIterable extends _TypeHelper {
276+
class _TypeHelperIterable extends _TypeHelperWithHelper {
256277
const _TypeHelperIterable();
257278

279+
@override
280+
String helperName(DartType paramType) => iterablesEqualHelperName;
281+
258282
@override
259283
String _decode(
260284
ParameterElement parameterElement, Set<String> pathParameters) {
@@ -324,6 +348,10 @@ $fieldName$nullAwareAccess.map((e) => e.toString()).toList()''';
324348
@override
325349
bool _matchesType(DartType type) =>
326350
const TypeChecker.fromRuntime(Iterable).isAssignableFromType(type);
351+
352+
@override
353+
String _compare(String value1, String value2) =>
354+
'!$iterablesEqualHelperName($value1, $value2)';
327355
}
328356

329357
abstract class _TypeHelperWithHelper extends _TypeHelper {

packages/go_router_builder/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: go_router_builder
22
description: >-
33
A builder that supports generated strongly-typed route helpers for
44
package:go_router
5-
version: 2.7.4
5+
version: 2.7.5
66
repository: https://github.com/flutter/packages/tree/main/packages/go_router_builder
77
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router_builder%22
88

packages/go_router_builder/test_inputs/iterable_with_default_value.dart.expect

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension $IterableDefaultValueRouteExtension on IterableDefaultValueRoute {
1313
String get location => GoRouteData.$location(
1414
'/iterable-default-value-route',
1515
queryParams: {
16-
if (param != const <int>[0])
16+
if (!_$iterablesEqual(param, const <int>[0]))
1717
'param': param.map((e) => e.toString()).toList(),
1818
},
1919
);
@@ -27,3 +27,17 @@ extension $IterableDefaultValueRouteExtension on IterableDefaultValueRoute {
2727

2828
void replace(BuildContext context) => context.replace(location);
2929
}
30+
31+
bool _$iterablesEqual<T>(Iterable<T>? iterable1, Iterable<T>? iterable2) {
32+
if (identical(iterable1, iterable2)) return true;
33+
if (iterable1 == null || iterable2 == null) return false;
34+
final iterator1 = iterable1.iterator;
35+
final iterator2 = iterable2.iterator;
36+
while (true) {
37+
final hasNext1 = iterator1.moveNext();
38+
final hasNext2 = iterator2.moveNext();
39+
if (hasNext1 != hasNext2) return false;
40+
if (!hasNext1) return true;
41+
if (iterator1.current != iterator2.current) return false;
42+
}
43+
}

packages/go_router_builder/test_inputs/list.dart.expect

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension $ListRouteExtension on ListRoute {
2323
'ids': ids.map((e) => e.toString()).toList(),
2424
if (nullableIds != null)
2525
'nullable-ids': nullableIds?.map((e) => e.toString()).toList(),
26-
if (idsWithDefaultValue != const <int>[0])
26+
if (!_$iterablesEqual(idsWithDefaultValue, const <int>[0]))
2727
'ids-with-default-value':
2828
idsWithDefaultValue.map((e) => e.toString()).toList(),
2929
},
@@ -38,3 +38,17 @@ extension $ListRouteExtension on ListRoute {
3838

3939
void replace(BuildContext context) => context.replace(location);
4040
}
41+
42+
bool _$iterablesEqual<T>(Iterable<T>? iterable1, Iterable<T>? iterable2) {
43+
if (identical(iterable1, iterable2)) return true;
44+
if (iterable1 == null || iterable2 == null) return false;
45+
final iterator1 = iterable1.iterator;
46+
final iterator2 = iterable2.iterator;
47+
while (true) {
48+
final hasNext1 = iterator1.moveNext();
49+
final hasNext2 = iterator2.moveNext();
50+
if (hasNext1 != hasNext2) return false;
51+
if (!hasNext1) return true;
52+
if (iterator1.current != iterator2.current) return false;
53+
}
54+
}

packages/go_router_builder/test_inputs/set.dart.expect

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension $SetRouteExtension on SetRoute {
2323
'ids': ids.map((e) => e.toString()).toList(),
2424
if (nullableIds != null)
2525
'nullable-ids': nullableIds?.map((e) => e.toString()).toList(),
26-
if (idsWithDefaultValue != const <int>{0})
26+
if (!_$iterablesEqual(idsWithDefaultValue, const <int>{0}))
2727
'ids-with-default-value':
2828
idsWithDefaultValue.map((e) => e.toString()).toList(),
2929
},
@@ -38,3 +38,17 @@ extension $SetRouteExtension on SetRoute {
3838

3939
void replace(BuildContext context) => context.replace(location);
4040
}
41+
42+
bool _$iterablesEqual<T>(Iterable<T>? iterable1, Iterable<T>? iterable2) {
43+
if (identical(iterable1, iterable2)) return true;
44+
if (iterable1 == null || iterable2 == null) return false;
45+
final iterator1 = iterable1.iterator;
46+
final iterator2 = iterable2.iterator;
47+
while (true) {
48+
final hasNext1 = iterator1.moveNext();
49+
final hasNext2 = iterator2.moveNext();
50+
if (hasNext1 != hasNext2) return false;
51+
if (!hasNext1) return true;
52+
if (iterator1.current != iterator2.current) return false;
53+
}
54+
}

0 commit comments

Comments
 (0)