mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +08:00
[go_router_builder] Adds support for enhanced enums. (#2395)
This commit is contained in:
@ -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`.
|
||||
|
@ -11,150 +11,353 @@ import 'shared/data.dart';
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
@ -9,57 +9,256 @@ part of 'all_types.dart';
|
||||
// **************************************************************************
|
||||
|
||||
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 @@ const _$PersonDetailsEnumMap = {
|
||||
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,
|
||||
|
@ -15,7 +15,7 @@ part 'main.g.dart';
|
||||
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 PersonDetailsRoute extends GoRouteData {
|
||||
}
|
||||
|
||||
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 HomeScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
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 FamilyScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
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 @@ class PersonDetailsPage extends StatelessWidget {
|
||||
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 PersonDetailsPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
const LoginScreen({this.from, Key? key}) : super(key: key);
|
||||
const LoginScreen({this.from, super.key});
|
||||
final String? from;
|
||||
|
||||
@override
|
||||
|
@ -14,6 +14,46 @@ enum PersonDetails {
|
||||
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({
|
||||
|
@ -14,7 +14,7 @@ part 'simple_example.g.dart';
|
||||
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 FamilyRoute extends GoRouteData {
|
||||
}
|
||||
|
||||
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 HomeScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -3,7 +3,7 @@ description: go_router_builder examples
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.14.0 <3.0.0"
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
114
packages/go_router_builder/example/test/all_types_test.dart
Normal file
114
packages/go_router_builder/example/test/all_types_test.dart
Normal file
@ -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);
|
||||
});
|
||||
}
|
@ -368,7 +368,7 @@ String _enumMapConst(InterfaceType type) {
|
||||
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)},',
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ name: go_router_builder
|
||||
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
|
||||
|
||||
|
@ -31,4 +31,5 @@ const Set<String> _expectedAnnotatedTests = <String>{
|
||||
'NullableRequiredParam',
|
||||
'UnsupportedType',
|
||||
'theAnswer',
|
||||
'EnumParam',
|
||||
};
|
||||
|
@ -70,3 +70,49 @@ class MissingPathParam extends GoRouteData {
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user