[go_router_builder] Adds support for enhanced enums. (#2395)

This commit is contained in:
Mirko Mucaria
2022-09-16 22:34:42 +02:00
committed by GitHub
parent a6f1b33af4
commit dfa1e65d77
12 changed files with 762 additions and 149 deletions

View File

@ -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`.

View File

@ -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,
);
}

View File

@ -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,

View File

@ -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

View File

@ -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({

View File

@ -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

View File

@ -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:

View 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);
});
}

View File

@ -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)},',
);

View File

@ -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

View File

@ -31,4 +31,5 @@ const Set<String> _expectedAnnotatedTests = <String>{
'NullableRequiredParam',
'UnsupportedType',
'theAnswer',
'EnumParam',
};

View File

@ -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;
}