Skip to content
Merged
3 changes: 2 additions & 1 deletion packages/go_router_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## NEXT
## 1.1.5

* Aligns Dart and Flutter SDK constraints.
* Supports default values for `Set`, `List` and `Iterable` route parameters.

## 1.1.4

Expand Down
57 changes: 57 additions & 0 deletions packages/go_router_builder/example/lib/all_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ part 'all_types.g.dart';
TypedGoRoute<StringRoute>(path: 'string-route/:requiredStringField'),
TypedGoRoute<UriRoute>(path: 'uri-route/:requiredUriField'),
TypedGoRoute<IterableRoute>(path: 'iterable-route'),
TypedGoRoute<IterableRouteWithDefaultValues>(
path: 'iterable-route-with-default-values'),
])
@immutable
class AllTypesBaseRoute extends GoRouteData {
Expand Down Expand Up @@ -342,6 +344,61 @@ class IterableRoute extends GoRouteData {
);
}

class IterableRouteWithDefaultValues extends GoRouteData {
IterableRouteWithDefaultValues({
this.intIterableField = const <int>[0],
this.doubleIterableField = const <double>[0, 1, 2],
this.stringIterableField = const <String>['defaultValue'],
this.boolIterableField = const <bool>[false],
this.enumIterableField = const <SportDetails>[
SportDetails.tennis,
SportDetails.hockey
],
this.intListField = const <int>[0],
this.doubleListField = const <double>[1, 2, 3],
this.stringListField = const <String>['defaultValue0', 'defaultValue1'],
this.boolListField = const <bool>[true],
this.enumListField = const <SportDetails>[SportDetails.football],
this.intSetField = const <int>{0, 1},
this.doubleSetField = const <double>{},
this.stringSetField = const <String>{'defaultValue'},
this.boolSetField = const <bool>{true, false},
this.enumSetField = const <SportDetails>{SportDetails.hockey},
});

final Iterable<int> intIterableField;
final List<int> intListField;
final Set<int> intSetField;

final Iterable<double> doubleIterableField;
final List<double> doubleListField;
final Set<double> doubleSetField;

final Iterable<String> stringIterableField;
final List<String> stringListField;
final Set<String> stringSetField;

final Iterable<bool> boolIterableField;
final List<bool> boolListField;
final Set<bool> boolSetField;

final Iterable<SportDetails> enumIterableField;
final List<SportDetails> enumListField;
final Set<SportDetails> enumSetField;

@override
Widget build(BuildContext context, GoRouterState state) =>
const BasePage<String>(
dataTitle: 'IterableRouteWithDefaultValues',
);

Widget drawerTile(BuildContext context) => ListTile(
title: const Text('IterableRouteWithDefaultValues'),
onTap: () => go(context),
selected: GoRouter.of(context).location == location,
);
}

class BasePage<T> extends StatelessWidget {
const BasePage({
required this.dataTitle,
Expand Down
118 changes: 118 additions & 0 deletions packages/go_router_builder/example/lib/all_types.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/go_router_builder/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ environment:
dependencies:
flutter:
sdk: flutter
go_router: ^6.0.0
go_router: ^6.2.0
provider: ^6.0.0

dev_dependencies:
Expand Down
29 changes: 13 additions & 16 deletions packages/go_router_builder/lib/src/type_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@ String decodeParameter(ParameterElement element) {
final DartType paramType = element.type;
for (final _TypeHelper helper in _helpers) {
if (helper._matchesType(paramType)) {
return helper._decode(element);
String decoded = helper._decode(element);
if (element.isOptional && element.hasDefaultValue) {
if (element.type.isNullableType) {
throw NullableDefaultValueError(element);
}
decoded += ' ?? ${element.defaultValueCode!}';
}
return decoded;
}
}

Expand Down Expand Up @@ -95,14 +102,7 @@ String _stateValueAccess(ParameterElement element) {
}

if (element.isOptional) {
String value = 'queryParams[${escapeDartString(element.name.kebab)}]';
if (element.hasDefaultValue) {
if (element.type.isNullableType) {
throw NullableDefaultValueError(element);
}
value += ' ?? ${element.defaultValueCode!}';
}
return value;
return 'queryParams[${escapeDartString(element.name.kebab)}]';
}

throw InvalidGenerationSourceError(
Expand Down Expand Up @@ -292,6 +292,7 @@ state.queryParametersAll[${escapeDartString(parameterElement.name.kebab)}]''';

@override
String _encode(String fieldName, DartType type) {
final String nullAwareAccess = type.isNullableType ? '?' : '';
if (type is ParameterizedType) {
final DartType iterableType = type.typeArguments.first;

Expand All @@ -300,15 +301,15 @@ state.queryParametersAll[${escapeDartString(parameterElement.name.kebab)}]''';
for (final _TypeHelper helper in _helpers) {
if (helper._matchesType(iterableType)) {
entriesTypeEncoder = '''
?.map((e) => ${helper._encode('e', iterableType)}).toList()''';
$nullAwareAccess.map((e) => ${helper._encode('e', iterableType)}).toList()''';
}
}
return '''
$fieldName$entriesTypeEncoder''';
}

return '''
$fieldName?.map((e) => e.toString()).toList()''';
$fieldName$nullAwareAccess.map((e) => e.toString()).toList()''';
}

@override
Expand All @@ -326,14 +327,10 @@ abstract class _TypeHelperWithHelper extends _TypeHelper {
final DartType paramType = parameterElement.type;

if (!parameterElement.isRequired) {
String decoded = '$convertMapValueHelperName('
return '$convertMapValueHelperName('
'${escapeDartString(parameterElement.name.kebab)}, '
'state.queryParams, '
'${helperName(paramType)})';
if (parameterElement.hasDefaultValue) {
decoded += ' ?? ${parameterElement.defaultValueCode!}';
}
return decoded;
}
return '${helperName(paramType)}'
'(state.${_stateValueAccess(parameterElement)})';
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router_builder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: go_router_builder
description: >-
A builder that supports generated strongly-typed route helpers for
package:go_router
version: 1.1.4
version: 1.1.5
repository: https://github.com/flutter/packages/tree/main/packages/go_router_builder
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router_builder%22

Expand Down
1 change: 1 addition & 0 deletions packages/go_router_builder/test/builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ const Set<String> _expectedAnnotatedTests = <String>{
'EnumParam',
'DefaultValueRoute',
'NullableDefaultValueRoute',
'IterableDefaultValueRoute',
};
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,38 @@ class NullableDefaultValueRoute extends GoRouteData {
NullableDefaultValueRoute({this.param = 0});
final int? param;
}

@ShouldGenerate(r'''
GoRoute get $iterableDefaultValueRoute => GoRouteData.$route(
path: '/iterable-default-value-route',
factory: $IterableDefaultValueRouteExtension._fromState,
);

extension $IterableDefaultValueRouteExtension on IterableDefaultValueRoute {
static IterableDefaultValueRoute _fromState(GoRouterState state) =>
IterableDefaultValueRoute(
param:
state.queryParametersAll['param']?.map(int.parse) ?? const <int>[0],
);

String get location => GoRouteData.$location(
'/iterable-default-value-route',
queryParams: {
if (param != const <int>[0])
'param': param.map((e) => e.toString()).toList(),
},
);

void go(BuildContext context) => context.go(location);

void push(BuildContext context) => context.push(location);

void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
}
''')
@TypedGoRoute<IterableDefaultValueRoute>(path: '/iterable-default-value-route')
class IterableDefaultValueRoute extends GoRouteData {
IterableDefaultValueRoute({this.param = const <int>[0]});
final Iterable<int> param;
}