Skip to content
Next Next commit
fixes #108437 support for iterable, list and set
  • Loading branch information
Skogsfrae committed Feb 22, 2023
commit b2c18aa5e63a9dbb4c2ca01ea839663c8655c4ac
54 changes: 54 additions & 0 deletions packages/go_router_builder/example/lib/all_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ part 'all_types.g.dart';
path: 'enhanced-enum-route/:requiredEnumField'),
TypedGoRoute<StringRoute>(path: 'string-route/:requiredStringField'),
TypedGoRoute<UriRoute>(path: 'uri-route/:requiredUriField'),
TypedGoRoute<IterableRoute>(path: 'iterable-route'),
])
@immutable
class AllTypesBaseRoute extends GoRouteData {
Expand Down Expand Up @@ -290,6 +291,59 @@ class UriRoute extends GoRouteData {
);
}

class IterableRoute extends GoRouteData {
IterableRoute({
this.intIterableField,
this.doubleIterableField,
this.stringIterableField,
this.boolIterableField,
this.enumIterableField,
this.intListField,
this.doubleListField,
this.stringListField,
this.boolListField,
this.enumListField,
this.intSetField,
this.doubleSetField,
this.stringSetField,
this.boolSetField,
this.enumSetField,
});

final Iterable<int>? intIterableField;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to modulalize this to be a class. I imagine someone would like to do this

class IterableRoute extends GoRouteData {
  IterableRoute({
    this.myField
  })
  final MyCustomField? myField;
}

abstract class CustomField<T> {
  Iterable<T> value;
  bool get isQueryParameter;
  String get queryParameterName;
}

class MyCustomField extend CustomField<List<int>> {
   MyCustomField();

   @override
    bool get isQueryParameter => true;
    @override
    String get queryParameterName => 'some-name' ;
}

The idea is to be able to choose the query parameter name.

Copy link
Contributor

@chunhtai chunhtai Nov 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Skogsfrae WDYT about this? The main concern is the query parameter names are hardcoded which I imagine people would ask for customized query parameter names in the future. Although this is not something we have to fix now, but the current API, as it is written, may not be able the extend to support this feature.

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) => BasePage<Iterable<int>>(
dataTitle: 'UriRoute',
param: const <int>[],
queryParam: intIterableField,
);

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

class BasePage<T> extends StatelessWidget {
const BasePage({
required this.dataTitle,
Expand Down
75 changes: 75 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.

47 changes: 47 additions & 0 deletions packages/go_router_builder/lib/src/type_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const String durationDecoderHelperName = r'_$duractionConverter';
/// The name of the generated, private helper for converting [String] to [Enum].
const String enumExtensionHelperName = r'_$fromName';

/// The name of the generated, private helper for converting [String] to [Enum].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be incorrect?

const String iterableDecoderHelperName = r'_$iterableConverter';

/// The property/parameter name used to represent the `extra` data that may
/// be passed to a route.
const String extraFieldName = r'$extra';
Expand All @@ -38,6 +41,7 @@ const List<_TypeHelper> _helpers = <_TypeHelper>[
_TypeHelperNum(),
_TypeHelperString(),
_TypeHelperUri(),
_TypeHelperIterable(),
];

/// Returns the decoded [String] value for [element], if its type is supported.
Expand Down Expand Up @@ -253,6 +257,49 @@ class _TypeHelperUri extends _TypeHelperWithHelper {
const TypeChecker.fromRuntime(Uri).isAssignableFromType(type);
}

class _TypeHelperIterable extends _TypeHelper {
const _TypeHelperIterable();

@override
String _decode(ParameterElement parameterElement) {
if (parameterElement.type is ParameterizedType) {
final DartType iterableType =
(parameterElement.type as ParameterizedType).typeArguments.first;

String entriesTypeMapper = '(e) => e';
for (final _TypeHelper helper in _helpers) {
if (helper._matchesType(iterableType) &&
helper is _TypeHelperWithHelper) {
entriesTypeMapper = helper.helperName(iterableType);
}
}

String iterableCaster = '';
if (const TypeChecker.fromRuntime(List)
.isAssignableFromType(parameterElement.type)) {
iterableCaster = '.toList()';
} else if (const TypeChecker.fromRuntime(Set)
.isAssignableFromType(parameterElement.type)) {
iterableCaster = '.toSet()';
}

return '''
state.queryParametersAll[
${escapeDartString(parameterElement.name.kebab)}]
?.map($entriesTypeMapper)$iterableCaster''';
}
return '''
state.queryParametersAll[${escapeDartString(parameterElement.name.kebab)}]''';
}

@override
String _encode(String fieldName, DartType type) => fieldName;

@override
bool _matchesType(DartType type) =>
const TypeChecker.fromRuntime(Iterable).isAssignableFromType(type);
}

abstract class _TypeHelperWithHelper extends _TypeHelper {
const _TypeHelperWithHelper();

Expand Down