Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[go_router_builder] Add support for Iterable, List and Set to TypedGoRoute #2679

Merged
4 changes: 4 additions & 0 deletions packages/go_router_builder/CHANGELOG.md
@@ -1,3 +1,7 @@
## 1.1.2

* Adds support for Iterables, Lists and Sets in query params for TypedGoRoute. [#108437](https://github.com/flutter/flutter/issues/108437).

## 1.1.1

* Support for the generation of the pushReplacement method has been added.
Expand Down
84 changes: 81 additions & 3 deletions packages/go_router_builder/example/lib/all_types.dart
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 All @@ -33,7 +34,6 @@ class AllTypesBaseRoute extends GoRouteData {
Widget build(BuildContext context, GoRouterState state) =>
const BasePage<void>(
dataTitle: 'Root',
param: null,
);
}

Expand Down Expand Up @@ -290,17 +290,69 @@ 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, GoRouterState state) =>
const BasePage<String>(
dataTitle: 'IterableRoute',
);

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

class BasePage<T> extends StatelessWidget {
const BasePage({
required this.dataTitle,
required this.param,
this.param,
this.queryParam,
this.queryParamWithDefaultValue,
super.key,
});

final String dataTitle;
final T param;
final T? param;
final T? queryParam;
final T? queryParamWithDefaultValue;

Expand Down Expand Up @@ -352,6 +404,32 @@ class BasePage<T> extends StatelessWidget {
requiredUriField: Uri.parse('https://dart.dev'),
uriField: Uri.parse('https://dart.dev'),
).drawerTile(context),
IterableRoute(
intIterableField: <int>[1, 2, 3],
doubleIterableField: <double>[.3, .4, .5],
stringIterableField: <String>['quo usque tandem'],
boolIterableField: <bool>[true, false, false],
enumIterableField: <SportDetails>[
SportDetails.football,
SportDetails.hockey,
],
intListField: <int>[1, 2, 3],
doubleListField: <double>[.3, .4, .5],
stringListField: <String>['quo usque tandem'],
boolListField: <bool>[true, false, false],
enumListField: <SportDetails>[
SportDetails.football,
SportDetails.hockey,
],
intSetField: <int>{1, 2, 3},
doubleSetField: <double>{.3, .4, .5},
stringSetField: <String>{'quo usque tandem'},
boolSetField: <bool>{true, false},
enumSetField: <SportDetails>{
SportDetails.football,
SportDetails.hockey,
},
).drawerTile(context),
],
)),
body: Center(
Expand Down
109 changes: 104 additions & 5 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/lib/main.g.dart

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

10 changes: 10 additions & 0 deletions packages/go_router_builder/example/test/all_types_test.dart
Expand Up @@ -125,5 +125,15 @@ void main() {
expect(find.text('UriRoute'), findsOneWidget);
expect(find.text('Param: https://dart.dev'), findsOneWidget);
expect(find.text('Query param: https://dart.dev'), findsOneWidget);

IterableRoute(
intListField: <int>[1, 2, 3],
).go(scaffoldState.context);
await tester.pumpAndSettle();
expect(find.text('IterableRoute'), findsOneWidget);
expect(
find.text(
'/iterable-route?int-list-field=1&int-list-field=2&int-list-field=3'),
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems a bit weird to hardcoded the query parameter name, I think this may make it less useful if a company care about the query parameter names. Would it make more sense to fix flutter/flutter#110781 and provide an API that people can set it up for iterable object with minimum code?

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 not addressed yet?

findsOneWidget);
});
}
5 changes: 4 additions & 1 deletion packages/go_router_builder/lib/src/route_config.dart
Expand Up @@ -215,7 +215,10 @@ GoRoute get $_routeGetterName => ${_routeDefinition()};
String get _locationArgs {
final Iterable<String> pathItems = _parsedPath.map((Token e) {
if (e is ParameterToken) {
return '\${Uri.encodeComponent(${_encodeFor(e.name)})}';
// Enum types are encoded using a map, so we need a nullability check
// here to ensure it matches Uri.encodeComponent nullability
final DartType? type = _field(e.name)?.returnType;
return '\${Uri.encodeComponent(${_encodeFor(e.name)}${type?.isEnum ?? false ? '!' : ''})}';
}
if (e is PathToken) {
return e.value;
Expand Down