[go_router_builder] Adds support for enhanced enums. (#2395)
diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md
index b04d17c..2ab6014 100644
--- a/packages/go_router_builder/CHANGELOG.md
+++ b/packages/go_router_builder/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.12
+
+* Adds support for enhanced enums. [#105876](https://github.com/flutter/flutter/issues/105876).
+
## 1.0.11
* Replaces mentions of the deprecated `GoRouteData.buildPage` with `GoRouteData.buildPageWithState`.
diff --git a/packages/go_router_builder/example/lib/all_types.dart b/packages/go_router_builder/example/lib/all_types.dart
index b9a35f9..8197697 100644
--- a/packages/go_router_builder/example/lib/all_types.dart
+++ b/packages/go_router_builder/example/lib/all_types.dart
@@ -11,150 +11,353 @@
part 'all_types.g.dart';
-@TypedGoRoute<AllTypesRoute>(
- path: '/:requiredBigIntField/:requiredBoolField/:requiredDateTimeField'
- '/:requiredDoubleField/:requiredEnumField/:requiredIntField'
- '/:requiredNumField/:requiredStringField/:requiredUriField',
-)
+@TypedGoRoute<AllTypesBaseRoute>(path: '/', routes: <TypedGoRoute<GoRouteData>>[
+ TypedGoRoute<BigIntRoute>(path: 'big-int-route/:requiredBigIntField'),
+ TypedGoRoute<BoolRoute>(path: 'bool-route/:requiredBoolField'),
+ TypedGoRoute<DateTimeRoute>(path: 'date-time-route/:requiredDateTimeField'),
+ TypedGoRoute<DoubleRoute>(path: 'double-route/:requiredDoubleField'),
+ TypedGoRoute<IntRoute>(path: 'int-route/:requiredIntField'),
+ TypedGoRoute<NumRoute>(path: 'num-route/:requiredNumField'),
+ TypedGoRoute<DoubleRoute>(path: 'double-route/:requiredDoubleField'),
+ TypedGoRoute<EnumRoute>(path: 'enum-route/:requiredEnumField'),
+ TypedGoRoute<EnhancedEnumRoute>(
+ path: 'enhanced-enum-route/:requiredEnumField'),
+ TypedGoRoute<StringRoute>(path: 'string-route/:requiredStringField'),
+ TypedGoRoute<UriRoute>(path: 'uri-route/:requiredUriField'),
+])
@immutable
-class AllTypesRoute extends GoRouteData {
- const AllTypesRoute({
+class AllTypesBaseRoute extends GoRouteData {
+ const AllTypesBaseRoute();
+
+ @override
+ Widget build(BuildContext context) => const BasePage<void>(
+ dataTitle: 'Root',
+ param: null,
+ );
+}
+
+class BigIntRoute extends GoRouteData {
+ BigIntRoute({
required this.requiredBigIntField,
- required this.requiredBoolField,
- required this.requiredDateTimeField,
- required this.requiredDoubleField,
- required this.requiredEnumField,
- required this.requiredIntField,
- required this.requiredNumField,
- required this.requiredStringField,
- required this.requiredUriField,
this.bigIntField,
- this.boolField,
- this.dateTimeField,
- this.doubleField,
- this.enumField,
- this.intField,
- this.numField,
- this.stringField,
- this.uriField,
});
final BigInt requiredBigIntField;
- final bool requiredBoolField;
- final DateTime requiredDateTimeField;
- final double requiredDoubleField;
- final PersonDetails requiredEnumField;
- final int requiredIntField;
- final num requiredNumField;
- final String requiredStringField;
- final Uri requiredUriField;
-
final BigInt? bigIntField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<BigInt>(
+ dataTitle: 'BigIntRoute',
+ param: requiredBigIntField,
+ queryParam: bigIntField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('BigIntRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class BoolRoute extends GoRouteData {
+ BoolRoute({
+ required this.requiredBoolField,
+ this.boolField,
+ });
+
+ final bool requiredBoolField;
final bool? boolField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<bool>(
+ dataTitle: 'BoolRoute',
+ param: requiredBoolField,
+ queryParam: boolField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('BoolRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class DateTimeRoute extends GoRouteData {
+ DateTimeRoute({
+ required this.requiredDateTimeField,
+ this.dateTimeField,
+ });
+
+ final DateTime requiredDateTimeField;
final DateTime? dateTimeField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<DateTime>(
+ dataTitle: 'DateTimeRoute',
+ param: requiredDateTimeField,
+ queryParam: dateTimeField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('DateTimeRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class DoubleRoute extends GoRouteData {
+ DoubleRoute({
+ required this.requiredDoubleField,
+ this.doubleField,
+ });
+
+ final double requiredDoubleField;
final double? doubleField;
- final PersonDetails? enumField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<double>(
+ dataTitle: 'DoubleRoute',
+ param: requiredDoubleField,
+ queryParam: doubleField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('DoubleRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class IntRoute extends GoRouteData {
+ IntRoute({
+ required this.requiredIntField,
+ this.intField,
+ });
+
+ final int requiredIntField;
final int? intField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<int>(
+ dataTitle: 'IntRoute',
+ param: requiredIntField,
+ queryParam: intField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('IntRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class NumRoute extends GoRouteData {
+ NumRoute({
+ required this.requiredNumField,
+ this.numField,
+ });
+
+ final num requiredNumField;
final num? numField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<num>(
+ dataTitle: 'NumRoute',
+ param: requiredNumField,
+ queryParam: numField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('NumRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class EnumRoute extends GoRouteData {
+ EnumRoute({
+ required this.requiredEnumField,
+ this.enumField,
+ });
+
+ final PersonDetails requiredEnumField;
+ final PersonDetails? enumField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<PersonDetails>(
+ dataTitle: 'EnumRoute',
+ param: requiredEnumField,
+ queryParam: enumField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('EnumRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class EnhancedEnumRoute extends GoRouteData {
+ EnhancedEnumRoute({
+ required this.requiredEnumField,
+ this.enumField,
+ });
+
+ final SportDetails requiredEnumField;
+ final SportDetails? enumField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<SportDetails>(
+ dataTitle: 'EnhancedEnumRoute',
+ param: requiredEnumField,
+ queryParam: enumField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('EnhancedEnumRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class StringRoute extends GoRouteData {
+ StringRoute({
+ required this.requiredStringField,
+ this.stringField,
+ });
+
+ final String requiredStringField;
final String? stringField;
+
+ @override
+ Widget build(BuildContext context) => BasePage<String>(
+ dataTitle: 'StringRoute',
+ param: requiredStringField,
+ queryParam: stringField,
+ );
+
+ Widget drawerTile(BuildContext context) => ListTile(
+ title: const Text('StringRoute'),
+ onTap: () => go(context),
+ selected: GoRouter.of(context).location == location,
+ );
+}
+
+class UriRoute extends GoRouteData {
+ UriRoute({
+ required this.requiredUriField,
+ this.uriField,
+ });
+
+ final Uri requiredUriField;
final Uri? uriField;
@override
+ Widget build(BuildContext context) => BasePage<Uri>(
+ dataTitle: 'UriRoute',
+ param: requiredUriField,
+ queryParam: uriField,
+ );
+
+ 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,
+ required this.param,
+ this.queryParam,
+ super.key,
+ });
+
+ final String dataTitle;
+ final T param;
+ final T? queryParam;
+
+ @override
Widget build(BuildContext context) => Scaffold(
+ appBar: AppBar(
+ title: const Text('Go router typed routes'),
+ ),
+ drawer: Drawer(
+ child: ListView(
+ children: <Widget>[
+ BigIntRoute(
+ requiredBigIntField: BigInt.two,
+ bigIntField: BigInt.zero,
+ ).drawerTile(context),
+ BoolRoute(
+ requiredBoolField: true,
+ boolField: false,
+ ).drawerTile(context),
+ DateTimeRoute(
+ requiredDateTimeField: DateTime(1970),
+ dateTimeField: DateTime(0),
+ ).drawerTile(context),
+ DoubleRoute(
+ requiredDoubleField: 3.14,
+ doubleField: -3.14,
+ ).drawerTile(context),
+ IntRoute(
+ requiredIntField: 42,
+ intField: -42,
+ ).drawerTile(context),
+ NumRoute(
+ requiredNumField: 2.71828,
+ numField: -2.71828,
+ ).drawerTile(context),
+ StringRoute(
+ requiredStringField: r'$!/#bob%%20',
+ stringField: r'$!/#bob%%20',
+ ).drawerTile(context),
+ EnumRoute(
+ requiredEnumField: PersonDetails.favoriteSport,
+ enumField: PersonDetails.favoriteFood,
+ ).drawerTile(context),
+ EnhancedEnumRoute(
+ requiredEnumField: SportDetails.football,
+ enumField: SportDetails.volleyball,
+ ).drawerTile(context),
+ UriRoute(
+ requiredUriField: Uri.parse('https://dart.dev'),
+ uriField: Uri.parse('https://dart.dev'),
+ ).drawerTile(context),
+ ],
+ )),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
- const Text('built!'),
- SelectableText(location),
+ const Text('Built!'),
+ Text(dataTitle),
+ Text('Param: $param'),
+ Text('Query param: $queryParam'),
+ SelectableText(GoRouter.of(context).location),
],
),
),
);
-
- @override
- int get hashCode => Object.hashAll(_items);
-
- @override
- bool operator ==(Object other) {
- if (other is AllTypesRoute) {
- final List<Object?> mine = _items;
- final List<Object?> theirs = other._items;
- for (int i = 0; i < mine.length; i++) {
- if (mine[i] != theirs[i]) {
- return false;
- }
- }
- }
- return true;
- }
-
- List<Object?> get _items => <Object?>[
- requiredBigIntField,
- requiredBoolField,
- requiredDateTimeField,
- requiredDoubleField,
- requiredEnumField,
- requiredIntField,
- requiredNumField,
- requiredStringField,
- requiredUriField,
- bigIntField,
- boolField,
- dateTimeField,
- doubleField,
- enumField,
- intField,
- numField,
- stringField,
- uriField,
- ];
}
void main() => runApp(AllTypesApp());
class AllTypesApp extends StatelessWidget {
- AllTypesApp({Key? key}) : super(key: key);
+ AllTypesApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
+ routeInformationProvider: _router.routeInformationProvider,
);
late final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
routes: $appRoutes,
-
- // redirect to the login page if the user is not logged in
- redirect: (GoRouterState state) {
- if (state.location == '/') {
- final String location = AllTypesRoute(
- requiredBigIntField: BigInt.two,
- requiredBoolField: true,
- requiredDateTimeField: DateTime.now(),
- requiredDoubleField: 3.14,
- requiredEnumField: PersonDetails.favoriteSport,
- requiredIntField: -42,
- requiredNumField: 3.15,
- requiredStringField: r'$!/#bob%%20',
- requiredUriField: Uri.parse('https://dart.dev'),
- bigIntField: BigInt.zero,
- boolField: false,
- dateTimeField: DateTime(0),
- doubleField: 3.14,
- enumField: PersonDetails.favoriteSport,
- intField: -42,
- numField: 3.15,
- stringField: r'$!/#bob%%20',
- uriField: Uri.parse('https://dart.dev'),
- ).location;
-
- return location;
- }
-
- // no need to redirect at all
- return null;
- },
+ initialLocation: const AllTypesBaseRoute().location,
);
}
diff --git a/packages/go_router_builder/example/lib/all_types.g.dart b/packages/go_router_builder/example/lib/all_types.g.dart
index 2643537..4aeb417 100644
--- a/packages/go_router_builder/example/lib/all_types.g.dart
+++ b/packages/go_router_builder/example/lib/all_types.g.dart
@@ -9,57 +9,256 @@
// **************************************************************************
List<GoRoute> get $appRoutes => [
- $allTypesRoute,
+ $allTypesBaseRoute,
];
-GoRoute get $allTypesRoute => GoRouteData.$route(
- path:
- '/:requiredBigIntField/:requiredBoolField/:requiredDateTimeField/:requiredDoubleField/:requiredEnumField/:requiredIntField/:requiredNumField/:requiredStringField/:requiredUriField',
- factory: $AllTypesRouteExtension._fromState,
+GoRoute get $allTypesBaseRoute => GoRouteData.$route(
+ path: '/',
+ factory: $AllTypesBaseRouteExtension._fromState,
+ routes: [
+ GoRouteData.$route(
+ path: 'big-int-route/:requiredBigIntField',
+ factory: $BigIntRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'bool-route/:requiredBoolField',
+ factory: $BoolRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'date-time-route/:requiredDateTimeField',
+ factory: $DateTimeRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'double-route/:requiredDoubleField',
+ factory: $DoubleRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'int-route/:requiredIntField',
+ factory: $IntRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'num-route/:requiredNumField',
+ factory: $NumRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'double-route/:requiredDoubleField',
+ factory: $DoubleRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'enum-route/:requiredEnumField',
+ factory: $EnumRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'enhanced-enum-route/:requiredEnumField',
+ factory: $EnhancedEnumRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'string-route/:requiredStringField',
+ factory: $StringRouteExtension._fromState,
+ ),
+ GoRouteData.$route(
+ path: 'uri-route/:requiredUriField',
+ factory: $UriRouteExtension._fromState,
+ ),
+ ],
);
-extension $AllTypesRouteExtension on AllTypesRoute {
- static AllTypesRoute _fromState(GoRouterState state) => AllTypesRoute(
+extension $AllTypesBaseRouteExtension on AllTypesBaseRoute {
+ static AllTypesBaseRoute _fromState(GoRouterState state) =>
+ const AllTypesBaseRoute();
+
+ String get location => GoRouteData.$location(
+ '/',
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $BigIntRouteExtension on BigIntRoute {
+ static BigIntRoute _fromState(GoRouterState state) => BigIntRoute(
requiredBigIntField: BigInt.parse(state.params['requiredBigIntField']!),
- requiredBoolField: _$boolConverter(state.params['requiredBoolField']!),
- requiredDateTimeField:
- DateTime.parse(state.params['requiredDateTimeField']!),
- requiredDoubleField: double.parse(state.params['requiredDoubleField']!),
- requiredEnumField: _$PersonDetailsEnumMap
- ._$fromName(state.params['requiredEnumField']!),
- requiredIntField: int.parse(state.params['requiredIntField']!),
- requiredNumField: num.parse(state.params['requiredNumField']!),
- requiredStringField: state.params['requiredStringField']!,
- requiredUriField: Uri.parse(state.params['requiredUriField']!),
bigIntField:
_$convertMapValue('big-int-field', state.queryParams, BigInt.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/big-int-route/${Uri.encodeComponent(requiredBigIntField.toString())}',
+ queryParams: {
+ if (bigIntField != null) 'big-int-field': bigIntField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $BoolRouteExtension on BoolRoute {
+ static BoolRoute _fromState(GoRouterState state) => BoolRoute(
+ requiredBoolField: _$boolConverter(state.params['requiredBoolField']!),
boolField:
_$convertMapValue('bool-field', state.queryParams, _$boolConverter),
+ );
+
+ String get location => GoRouteData.$location(
+ '/bool-route/${Uri.encodeComponent(requiredBoolField.toString())}',
+ queryParams: {
+ if (boolField != null) 'bool-field': boolField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $DateTimeRouteExtension on DateTimeRoute {
+ static DateTimeRoute _fromState(GoRouterState state) => DateTimeRoute(
+ requiredDateTimeField:
+ DateTime.parse(state.params['requiredDateTimeField']!),
dateTimeField: _$convertMapValue(
'date-time-field', state.queryParams, DateTime.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/date-time-route/${Uri.encodeComponent(requiredDateTimeField.toString())}',
+ queryParams: {
+ if (dateTimeField != null)
+ 'date-time-field': dateTimeField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $DoubleRouteExtension on DoubleRoute {
+ static DoubleRoute _fromState(GoRouterState state) => DoubleRoute(
+ requiredDoubleField: double.parse(state.params['requiredDoubleField']!),
doubleField:
_$convertMapValue('double-field', state.queryParams, double.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/double-route/${Uri.encodeComponent(requiredDoubleField.toString())}',
+ queryParams: {
+ if (doubleField != null) 'double-field': doubleField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $IntRouteExtension on IntRoute {
+ static IntRoute _fromState(GoRouterState state) => IntRoute(
+ requiredIntField: int.parse(state.params['requiredIntField']!),
+ intField: _$convertMapValue('int-field', state.queryParams, int.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/int-route/${Uri.encodeComponent(requiredIntField.toString())}',
+ queryParams: {
+ if (intField != null) 'int-field': intField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $NumRouteExtension on NumRoute {
+ static NumRoute _fromState(GoRouterState state) => NumRoute(
+ requiredNumField: num.parse(state.params['requiredNumField']!),
+ numField: _$convertMapValue('num-field', state.queryParams, num.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/num-route/${Uri.encodeComponent(requiredNumField.toString())}',
+ queryParams: {
+ if (numField != null) 'num-field': numField!.toString(),
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $EnumRouteExtension on EnumRoute {
+ static EnumRoute _fromState(GoRouterState state) => EnumRoute(
+ requiredEnumField: _$PersonDetailsEnumMap
+ ._$fromName(state.params['requiredEnumField']!),
enumField: _$convertMapValue(
'enum-field', state.queryParams, _$PersonDetailsEnumMap._$fromName),
- intField: _$convertMapValue('int-field', state.queryParams, int.parse),
- numField: _$convertMapValue('num-field', state.queryParams, num.parse),
+ );
+
+ String get location => GoRouteData.$location(
+ '/enum-route/${Uri.encodeComponent(_$PersonDetailsEnumMap[requiredEnumField]!)}',
+ queryParams: {
+ if (enumField != null)
+ 'enum-field': _$PersonDetailsEnumMap[enumField!]!,
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $EnhancedEnumRouteExtension on EnhancedEnumRoute {
+ static EnhancedEnumRoute _fromState(GoRouterState state) => EnhancedEnumRoute(
+ requiredEnumField: _$SportDetailsEnumMap
+ ._$fromName(state.params['requiredEnumField']!),
+ enumField: _$convertMapValue(
+ 'enum-field', state.queryParams, _$SportDetailsEnumMap._$fromName),
+ );
+
+ String get location => GoRouteData.$location(
+ '/enhanced-enum-route/${Uri.encodeComponent(_$SportDetailsEnumMap[requiredEnumField]!)}',
+ queryParams: {
+ if (enumField != null)
+ 'enum-field': _$SportDetailsEnumMap[enumField!]!,
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $StringRouteExtension on StringRoute {
+ static StringRoute _fromState(GoRouterState state) => StringRoute(
+ requiredStringField: state.params['requiredStringField']!,
stringField: state.queryParams['string-field'],
+ );
+
+ String get location => GoRouteData.$location(
+ '/string-route/${Uri.encodeComponent(requiredStringField)}',
+ queryParams: {
+ if (stringField != null) 'string-field': stringField!,
+ },
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+extension $UriRouteExtension on UriRoute {
+ static UriRoute _fromState(GoRouterState state) => UriRoute(
+ requiredUriField: Uri.parse(state.params['requiredUriField']!),
uriField: _$convertMapValue('uri-field', state.queryParams, Uri.parse),
);
String get location => GoRouteData.$location(
- '/${Uri.encodeComponent(requiredBigIntField.toString())}/${Uri.encodeComponent(requiredBoolField.toString())}/${Uri.encodeComponent(requiredDateTimeField.toString())}/${Uri.encodeComponent(requiredDoubleField.toString())}/${Uri.encodeComponent(_$PersonDetailsEnumMap[requiredEnumField]!)}/${Uri.encodeComponent(requiredIntField.toString())}/${Uri.encodeComponent(requiredNumField.toString())}/${Uri.encodeComponent(requiredStringField)}/${Uri.encodeComponent(requiredUriField.toString())}',
+ '/uri-route/${Uri.encodeComponent(requiredUriField.toString())}',
queryParams: {
- if (bigIntField != null) 'big-int-field': bigIntField!.toString(),
- if (boolField != null) 'bool-field': boolField!.toString(),
- if (dateTimeField != null)
- 'date-time-field': dateTimeField!.toString(),
- if (doubleField != null) 'double-field': doubleField!.toString(),
- if (enumField != null)
- 'enum-field': _$PersonDetailsEnumMap[enumField!]!,
- if (intField != null) 'int-field': intField!.toString(),
- if (numField != null) 'num-field': numField!.toString(),
- if (stringField != null) 'string-field': stringField!,
if (uriField != null) 'uri-field': uriField!.toString(),
},
);
@@ -75,6 +274,13 @@
PersonDetails.favoriteSport: 'favorite-sport',
};
+const _$SportDetailsEnumMap = {
+ SportDetails.volleyball: 'volleyball',
+ SportDetails.football: 'football',
+ SportDetails.tennis: 'tennis',
+ SportDetails.hockey: 'hockey',
+};
+
T? _$convertMapValue<T>(
String key,
Map<String, String> map,
diff --git a/packages/go_router_builder/example/lib/main.dart b/packages/go_router_builder/example/lib/main.dart
index e58a5ac..1023f8c 100644
--- a/packages/go_router_builder/example/lib/main.dart
+++ b/packages/go_router_builder/example/lib/main.dart
@@ -15,7 +15,7 @@
void main() => runApp(App());
class App extends StatelessWidget {
- App({Key? key}) : super(key: key);
+ App({super.key});
final LoginInfo loginInfo = LoginInfo();
static const String title = 'GoRouter Example: Named Routes';
@@ -148,7 +148,7 @@
}
class HomeScreen extends StatelessWidget {
- const HomeScreen({Key? key}) : super(key: key);
+ const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
@@ -184,7 +184,7 @@
}
class FamilyScreen extends StatelessWidget {
- const FamilyScreen({required this.family, Key? key}) : super(key: key);
+ const FamilyScreen({required this.family, super.key});
final Family family;
@override
@@ -203,8 +203,7 @@
}
class PersonScreen extends StatelessWidget {
- const PersonScreen({required this.family, required this.person, Key? key})
- : super(key: key);
+ const PersonScreen({required this.family, required this.person, super.key});
final Family family;
final Person person;
@@ -250,8 +249,8 @@
required this.person,
required this.detailsKey,
this.extra,
- Key? key,
- }) : super(key: key);
+ super.key,
+ });
final Family family;
final Person person;
@@ -278,7 +277,7 @@
}
class LoginScreen extends StatelessWidget {
- const LoginScreen({this.from, Key? key}) : super(key: key);
+ const LoginScreen({this.from, super.key});
final String? from;
@override
diff --git a/packages/go_router_builder/example/lib/shared/data.dart b/packages/go_router_builder/example/lib/shared/data.dart
index 3f017fc..edc3f20 100644
--- a/packages/go_router_builder/example/lib/shared/data.dart
+++ b/packages/go_router_builder/example/lib/shared/data.dart
@@ -14,6 +14,46 @@
favoriteSport,
}
+enum SportDetails {
+ volleyball(
+ imageUrl: '/sportdetails/url/volleyball.jpg',
+ playerPerTeam: 6,
+ accessory: null,
+ hasNet: true,
+ ),
+ football(
+ imageUrl: '/sportdetails/url/Football.jpg',
+ playerPerTeam: 11,
+ accessory: null,
+ hasNet: true,
+ ),
+ tennis(
+ imageUrl: '/sportdetails/url/tennis.jpg',
+ playerPerTeam: 2,
+ accessory: 'Rackets',
+ hasNet: true,
+ ),
+ hockey(
+ imageUrl: '/sportdetails/url/hockey.jpg',
+ playerPerTeam: 6,
+ accessory: 'Hockey sticks',
+ hasNet: true,
+ ),
+ ;
+
+ const SportDetails({
+ required this.accessory,
+ required this.hasNet,
+ required this.imageUrl,
+ required this.playerPerTeam,
+ });
+
+ final String imageUrl;
+ final int playerPerTeam;
+ final String? accessory;
+ final bool hasNet;
+}
+
/// sample Person class
class Person {
Person({
diff --git a/packages/go_router_builder/example/lib/simple_example.dart b/packages/go_router_builder/example/lib/simple_example.dart
index 1597104..9657475 100644
--- a/packages/go_router_builder/example/lib/simple_example.dart
+++ b/packages/go_router_builder/example/lib/simple_example.dart
@@ -14,7 +14,7 @@
void main() => runApp(App());
class App extends StatelessWidget {
- App({Key? key}) : super(key: key);
+ App({super.key});
@override
Widget build(BuildContext context) => MaterialApp.router(
@@ -50,7 +50,7 @@
}
class HomeScreen extends StatelessWidget {
- const HomeScreen({Key? key}) : super(key: key);
+ const HomeScreen({super.key});
@override
Widget build(BuildContext context) => Scaffold(
@@ -68,7 +68,7 @@
}
class FamilyScreen extends StatelessWidget {
- const FamilyScreen({required this.family, Key? key}) : super(key: key);
+ const FamilyScreen({required this.family, super.key});
final Family family;
@override
diff --git a/packages/go_router_builder/example/pubspec.yaml b/packages/go_router_builder/example/pubspec.yaml
index 90edafe..4a4f72f 100644
--- a/packages/go_router_builder/example/pubspec.yaml
+++ b/packages/go_router_builder/example/pubspec.yaml
@@ -3,7 +3,7 @@
publish_to: none
environment:
- sdk: ">=2.14.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
dependencies:
flutter:
diff --git a/packages/go_router_builder/example/test/all_types_test.dart b/packages/go_router_builder/example/test/all_types_test.dart
new file mode 100644
index 0000000..4a0cac6
--- /dev/null
+++ b/packages/go_router_builder/example/test/all_types_test.dart
@@ -0,0 +1,114 @@
+// 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/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router_builder_example/all_types.dart';
+import 'package:go_router_builder_example/shared/data.dart';
+
+void main() {
+ testWidgets('Test typed route navigation', (WidgetTester tester) async {
+ await tester.pumpWidget(AllTypesApp());
+
+ final ScaffoldState scaffoldState =
+ tester.firstState(find.byType(Scaffold));
+
+ BigIntRoute(
+ requiredBigIntField: BigInt.from(4),
+ bigIntField: BigInt.from(8),
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('BigIntRoute'), findsOneWidget);
+ expect(find.text('Param: 4'), findsOneWidget);
+ expect(find.text('Query param: 8'), findsOneWidget);
+
+ BoolRoute(
+ requiredBoolField: false,
+ boolField: true,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('BoolRoute'), findsOneWidget);
+ expect(find.text('Param: false'), findsOneWidget);
+ expect(find.text('Query param: true'), findsOneWidget);
+
+ final DateTime param = DateTime.now();
+ final DateTime query = DateTime(2017, 9, 7, 17, 30);
+ DateTimeRoute(
+ requiredDateTimeField: param,
+ dateTimeField: query,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('DateTimeRoute'), findsOneWidget);
+ expect(find.text('Param: $param'), findsOneWidget);
+ expect(find.text('Query param: $query'), findsOneWidget);
+
+ DoubleRoute(
+ requiredDoubleField: 3.14,
+ doubleField: -3.14,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('DoubleRoute'), findsOneWidget);
+ expect(find.text('Param: 3.14'), findsOneWidget);
+ expect(find.text('Query param: -3.14'), findsOneWidget);
+
+ IntRoute(
+ requiredIntField: 65,
+ intField: -65,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('IntRoute'), findsOneWidget);
+ expect(find.text('Param: 65'), findsOneWidget);
+ expect(find.text('Query param: -65'), findsOneWidget);
+
+ NumRoute(
+ requiredNumField: 987.32,
+ numField: -987.32,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('NumRoute'), findsOneWidget);
+ expect(find.text('Param: 987.32'), findsOneWidget);
+ expect(find.text('Query param: -987.32'), findsOneWidget);
+
+ StringRoute(
+ requiredStringField: r'Tytire tu patulae recubans sub tegmine fagi.',
+ stringField: r'Tytire tu patulae recubans sub tegmine fagi.',
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('StringRoute'), findsOneWidget);
+ expect(find.text('Param: Tytire tu patulae recubans sub tegmine fagi.'),
+ findsOneWidget);
+ expect(
+ find.text('Query param: Tytire tu patulae recubans sub tegmine fagi.'),
+ findsOneWidget);
+
+ EnumRoute(
+ requiredEnumField: PersonDetails.favoriteFood,
+ enumField: PersonDetails.favoriteSport,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('EnumRoute'), findsOneWidget);
+ expect(find.text('Param: PersonDetails.favoriteFood'), findsOneWidget);
+ expect(
+ find.text('Query param: PersonDetails.favoriteSport'), findsOneWidget);
+
+ EnhancedEnumRoute(
+ requiredEnumField: SportDetails.football,
+ enumField: SportDetails.hockey,
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('EnhancedEnumRoute'), findsOneWidget);
+ expect(find.text('Param: SportDetails.football'), findsOneWidget);
+ expect(find.text('Query param: SportDetails.hockey'), findsOneWidget);
+
+ UriRoute(
+ requiredUriField: Uri.parse('https://dart.dev'),
+ uriField: Uri.parse('https://dart.dev'),
+ ).go(scaffoldState.context);
+ await tester.pumpAndSettle();
+ expect(find.text('UriRoute'), findsOneWidget);
+ expect(find.text('Param: https://dart.dev'), findsOneWidget);
+ expect(find.text('Query param: https://dart.dev'), findsOneWidget);
+ });
+}
diff --git a/packages/go_router_builder/lib/src/route_config.dart b/packages/go_router_builder/lib/src/route_config.dart
index 49ebc5d..f058a2c 100644
--- a/packages/go_router_builder/lib/src/route_config.dart
+++ b/packages/go_router_builder/lib/src/route_config.dart
@@ -368,7 +368,7 @@
final StringBuffer buffer = StringBuffer('const ${enumMapName(type)} = {');
for (final FieldElement enumField in type.element2.fields
- .where((FieldElement element) => !element.isSynthetic)) {
+ .where((FieldElement element) => element.isEnumConstant)) {
buffer.writeln(
'$enumName.${enumField.name}: ${escapeDartString(enumField.name.kebab)},',
);
diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml
index ac221ea..81781eb 100644
--- a/packages/go_router_builder/pubspec.yaml
+++ b/packages/go_router_builder/pubspec.yaml
@@ -2,7 +2,7 @@
description: >-
A builder that supports generated strongly-typed route helpers for
package:go_router
-version: 1.0.11
+version: 1.0.12
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
diff --git a/packages/go_router_builder/test/builder_test.dart b/packages/go_router_builder/test/builder_test.dart
index bcd25d2..147e825 100644
--- a/packages/go_router_builder/test/builder_test.dart
+++ b/packages/go_router_builder/test/builder_test.dart
@@ -31,4 +31,5 @@
'NullableRequiredParam',
'UnsupportedType',
'theAnswer',
+ 'EnumParam',
};
diff --git a/packages/go_router_builder/test/test_inputs/_go_router_builder_test_input.dart b/packages/go_router_builder/test/test_inputs/_go_router_builder_test_input.dart
index 03a7ded..ca204ef 100644
--- a/packages/go_router_builder/test/test_inputs/_go_router_builder_test_input.dart
+++ b/packages/go_router_builder/test/test_inputs/_go_router_builder_test_input.dart
@@ -70,3 +70,49 @@
MissingPathParam({required this.id});
final String id;
}
+
+@ShouldGenerate(r'''
+GoRoute get $enumParam => GoRouteData.$route(
+ path: '/:y',
+ factory: $EnumParamExtension._fromState,
+ );
+
+extension $EnumParamExtension on EnumParam {
+ static EnumParam _fromState(GoRouterState state) => EnumParam(
+ y: _$EnumTestEnumMap._$fromName(state.params['y']!),
+ );
+
+ String get location => GoRouteData.$location(
+ '/${Uri.encodeComponent(_$EnumTestEnumMap[y]!)}',
+ );
+
+ void go(BuildContext context) => context.go(location, extra: this);
+
+ void push(BuildContext context) => context.push(location, extra: this);
+}
+
+const _$EnumTestEnumMap = {
+ EnumTest.a: 'a',
+ EnumTest.b: 'b',
+ EnumTest.c: 'c',
+};
+
+extension<T extends Enum> on Map<T, String> {
+ T _$fromName(String value) =>
+ entries.singleWhere((element) => element.value == value).key;
+}
+''')
+@TypedGoRoute<EnumParam>(path: '/:y')
+class EnumParam extends GoRouteData {
+ EnumParam({required this.y});
+ final EnumTest y;
+}
+
+enum EnumTest {
+ a(1),
+ b(3),
+ c(5);
+
+ const EnumTest(this.x);
+ final int x;
+}