diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index d2ad363cf89..8004affe809 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.2.0 + +- Adds support for`extension type`. + ## 3.1.0 - Updates dependencies to use the latest `analyzer`, `build`, and `source_gen`. diff --git a/packages/go_router_builder/README.md b/packages/go_router_builder/README.md index 62a8d94b3af..1a683bb4784 100644 --- a/packages/go_router_builder/README.md +++ b/packages/go_router_builder/README.md @@ -334,7 +334,7 @@ class RedirectRoute extends GoRouteData { ## Type conversions -The code generator can convert simple types like `int` and `enum` to/from the +The code generator can convert simple types like `int`, `enum`, and `extension type` to/from the `String` type of the underlying pathParameters: diff --git a/packages/go_router_builder/example/lib/all_extension_types.dart b/packages/go_router_builder/example/lib/all_extension_types.dart new file mode 100644 index 00000000000..a9949cd1eb5 --- /dev/null +++ b/packages/go_router_builder/example/lib/all_extension_types.dart @@ -0,0 +1,408 @@ +// 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. + +// ignore_for_file: public_member_api_docs, unreachable_from_main + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'shared/data.dart'; + +part 'all_extension_types.g.dart'; + +@TypedGoRoute(path: '/', routes: >[ + TypedGoRoute( + path: 'big-int-route/:requiredBigIntField'), + TypedGoRoute(path: 'bool-route/:requiredBoolField'), + TypedGoRoute( + path: 'date-time-route/:requiredDateTimeField'), + TypedGoRoute(path: 'double-route/:requiredDoubleField'), + TypedGoRoute(path: 'int-route/:requiredIntField'), + TypedGoRoute(path: 'num-route/:requiredNumField'), + TypedGoRoute(path: 'double-route/:requiredDoubleField'), + TypedGoRoute(path: 'enum-route/:requiredEnumField'), + TypedGoRoute( + path: 'enhanced-enum-route/:requiredEnumField'), + TypedGoRoute(path: 'string-route/:requiredStringField'), + TypedGoRoute(path: 'uri-route/:requiredUriField'), +]) +@immutable +class AllTypesBaseRoute extends GoRouteData with _$AllTypesBaseRoute { + const AllTypesBaseRoute(); + + @override + Widget build(BuildContext context, GoRouterState state) => + const BasePage( + dataTitle: 'Root', + ); +} + +extension type const BigIntExtension(BigInt value) {} +extension type const BoolExtension(bool value) {} +extension type const DateTimeExtension(DateTime value) {} +extension type const DoubleExtension(double value) {} +extension type const IntExtension(int value) {} +extension type const NumExtension(num value) {} +extension type const StringExtension(String value) {} +extension type const UriExtension(Uri value) {} +extension type const PersonDetailsExtension(PersonDetails value) {} +extension type const SportDetailsExtension(SportDetails value) {} + +class BigIntExtensionRoute extends GoRouteData with _$BigIntExtensionRoute { + const BigIntExtensionRoute({ + required this.requiredBigIntField, + this.bigIntField, + }); + + final BigIntExtension requiredBigIntField; + final BigIntExtension? bigIntField; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'BigIntExtensionRoute', + param: requiredBigIntField.value, + queryParam: bigIntField?.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('BigIntExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class BoolExtensionRoute extends GoRouteData with _$BoolExtensionRoute { + const BoolExtensionRoute({ + required this.requiredBoolField, + this.boolField, + this.boolFieldWithDefaultValue = const BoolExtension(true), + }); + + final BoolExtension requiredBoolField; + final BoolExtension? boolField; + final BoolExtension boolFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'BoolExtensionRoute', + param: requiredBoolField.value, + queryParam: boolField?.value, + queryParamWithDefaultValue: boolFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('BoolExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class DateTimeExtensionRoute extends GoRouteData with _$DateTimeExtensionRoute { + const DateTimeExtensionRoute({ + required this.requiredDateTimeField, + this.dateTimeField, + }); + + final DateTimeExtension requiredDateTimeField; + final DateTimeExtension? dateTimeField; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'DateTimeExtensionRoute', + param: requiredDateTimeField.value, + queryParam: dateTimeField?.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('DateTimeExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class DoubleExtensionRoute extends GoRouteData with _$DoubleExtensionRoute { + const DoubleExtensionRoute({ + required this.requiredDoubleField, + this.doubleField, + this.doubleFieldWithDefaultValue = const DoubleExtension(1.0), + }); + + final DoubleExtension requiredDoubleField; + final DoubleExtension? doubleField; + final DoubleExtension doubleFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'DoubleExtensionRoute', + param: requiredDoubleField.value, + queryParam: doubleField?.value, + queryParamWithDefaultValue: doubleFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('DoubleExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class IntExtensionRoute extends GoRouteData with _$IntExtensionRoute { + const IntExtensionRoute({ + required this.requiredIntField, + this.intField, + this.intFieldWithDefaultValue = const IntExtension(1), + }); + + final IntExtension requiredIntField; + final IntExtension? intField; + final IntExtension intFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'IntExtensionRoute', + param: requiredIntField.value, + queryParam: intField?.value, + queryParamWithDefaultValue: intFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('IntExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class NumExtensionRoute extends GoRouteData with _$NumExtensionRoute { + const NumExtensionRoute({ + required this.requiredNumField, + this.numField, + this.numFieldWithDefaultValue = const NumExtension(1), + }); + + final NumExtension requiredNumField; + final NumExtension? numField; + final NumExtension numFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'NumExtensionRoute', + param: requiredNumField.value, + queryParam: numField?.value, + queryParamWithDefaultValue: numFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('NumExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class EnumExtensionRoute extends GoRouteData with _$EnumExtensionRoute { + const EnumExtensionRoute({ + required this.requiredEnumField, + this.enumField, + this.enumFieldWithDefaultValue = + const PersonDetailsExtension(PersonDetails.favoriteFood), + }); + + final PersonDetailsExtension requiredEnumField; + final PersonDetailsExtension? enumField; + final PersonDetailsExtension enumFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => + BasePage( + dataTitle: 'EnumExtensionRoute', + param: requiredEnumField.value, + queryParam: enumField?.value, + queryParamWithDefaultValue: enumFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('EnumExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class EnhancedEnumExtensionRoute extends GoRouteData + with _$EnhancedEnumExtensionRoute { + const EnhancedEnumExtensionRoute({ + required this.requiredEnumField, + this.enumField, + this.enumFieldWithDefaultValue = + const SportDetailsExtension(SportDetails.football), + }); + + final SportDetailsExtension requiredEnumField; + final SportDetailsExtension? enumField; + final SportDetailsExtension enumFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => + BasePage( + dataTitle: 'EnhancedEnumExtensionRoute', + param: requiredEnumField.value, + queryParam: enumField?.value, + queryParamWithDefaultValue: enumFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('EnhancedEnumExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class StringExtensionRoute extends GoRouteData with _$StringExtensionRoute { + const StringExtensionRoute({ + required this.requiredStringField, + this.stringField, + this.stringFieldWithDefaultValue = const StringExtension('defaultValue'), + }); + + final StringExtension requiredStringField; + final StringExtension? stringField; + final StringExtension stringFieldWithDefaultValue; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'StringExtensionRoute', + param: requiredStringField.value, + queryParam: stringField?.value, + queryParamWithDefaultValue: stringFieldWithDefaultValue.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('StringExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class UriExtensionRoute extends GoRouteData with _$UriExtensionRoute { + const UriExtensionRoute({ + required this.requiredUriField, + this.uriField, + }); + + final UriExtension requiredUriField; + final UriExtension? uriField; + + @override + Widget build(BuildContext context, GoRouterState state) => BasePage( + dataTitle: 'UriExtensionRoute', + param: requiredUriField.value, + queryParam: uriField?.value, + ); + + Widget drawerTile(BuildContext context) => ListTile( + title: const Text('UriExtensionRoute'), + onTap: () => go(context), + selected: GoRouterState.of(context).uri.path == location, + ); +} + +class BasePage extends StatelessWidget { + const BasePage({ + required this.dataTitle, + this.param, + this.queryParam, + this.queryParamWithDefaultValue, + super.key, + }); + + final String dataTitle; + final T? param; + final T? queryParam; + final T? queryParamWithDefaultValue; + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text('Go router extension types'), + ), + drawer: Drawer( + child: ListView( + children: [ + BigIntExtensionRoute( + requiredBigIntField: BigIntExtension(BigInt.two), + bigIntField: BigIntExtension(BigInt.zero), + ).drawerTile(context), + const BoolExtensionRoute( + requiredBoolField: BoolExtension(true), + boolField: BoolExtension(false), + ).drawerTile(context), + DateTimeExtensionRoute( + requiredDateTimeField: DateTimeExtension(DateTime(1970)), + dateTimeField: DateTimeExtension(DateTime(0)), + ).drawerTile(context), + const DoubleExtensionRoute( + requiredDoubleField: DoubleExtension(3.14), + doubleField: DoubleExtension(-3.14), + ).drawerTile(context), + const IntExtensionRoute( + requiredIntField: IntExtension(42), + intField: IntExtension(-42), + ).drawerTile(context), + const NumExtensionRoute( + requiredNumField: NumExtension(2.71828), + numField: NumExtension(-2.71828), + ).drawerTile(context), + const StringExtensionRoute( + requiredStringField: StringExtension(r'$!/#bob%%20'), + stringField: StringExtension(r'$!/#bob%%20'), + ).drawerTile(context), + const EnumExtensionRoute( + requiredEnumField: + PersonDetailsExtension(PersonDetails.favoriteSport), + enumField: PersonDetailsExtension(PersonDetails.favoriteFood), + ).drawerTile(context), + const EnhancedEnumExtensionRoute( + requiredEnumField: SportDetailsExtension(SportDetails.football), + enumField: SportDetailsExtension(SportDetails.volleyball), + ).drawerTile(context), + UriExtensionRoute( + requiredUriField: UriExtension(Uri.parse('https://dart.dev')), + uriField: UriExtension(Uri.parse('https://dart.dev')), + ).drawerTile(context), + ], + )), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Built with Extension Types!'), + Text(dataTitle), + Text('Param: $param'), + Text('Query param: $queryParam'), + Text( + 'Query param with default value: $queryParamWithDefaultValue', + ), + SelectableText(GoRouterState.of(context).uri.path), + SelectableText( + GoRouterState.of(context).uri.queryParameters.toString()), + ], + ), + ), + ); +} + +void main() => runApp(AllExtensionTypesApp()); + +class AllExtensionTypesApp extends StatelessWidget { + AllExtensionTypesApp({super.key}); + + @override + Widget build(BuildContext context) => MaterialApp.router( + routerConfig: _router, + ); + + late final GoRouter _router = GoRouter( + debugLogDiagnostics: true, + routes: $appRoutes, + initialLocation: const AllTypesBaseRoute().location, + ); +} diff --git a/packages/go_router_builder/example/lib/all_extension_types.g.dart b/packages/go_router_builder/example/lib/all_extension_types.g.dart new file mode 100644 index 00000000000..999e5bc607d --- /dev/null +++ b/packages/go_router_builder/example/lib/all_extension_types.g.dart @@ -0,0 +1,502 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: always_specify_types, public_member_api_docs + +part of 'all_extension_types.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $allTypesBaseRoute, + ]; + +RouteBase get $allTypesBaseRoute => GoRouteData.$route( + path: '/', + factory: _$AllTypesBaseRoute._fromState, + routes: [ + GoRouteData.$route( + path: 'big-int-route/:requiredBigIntField', + factory: _$BigIntExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'bool-route/:requiredBoolField', + factory: _$BoolExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'date-time-route/:requiredDateTimeField', + factory: _$DateTimeExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'double-route/:requiredDoubleField', + factory: _$DoubleExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'int-route/:requiredIntField', + factory: _$IntExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'num-route/:requiredNumField', + factory: _$NumExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'double-route/:requiredDoubleField', + factory: _$DoubleExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'enum-route/:requiredEnumField', + factory: _$EnumExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'enhanced-enum-route/:requiredEnumField', + factory: _$EnhancedEnumExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'string-route/:requiredStringField', + factory: _$StringExtensionRoute._fromState, + ), + GoRouteData.$route( + path: 'uri-route/:requiredUriField', + factory: _$UriExtensionRoute._fromState, + ), + ], + ); + +mixin _$AllTypesBaseRoute on GoRouteData { + static AllTypesBaseRoute _fromState(GoRouterState state) => + const AllTypesBaseRoute(); + + @override + String get location => GoRouteData.$location( + '/', + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$BigIntExtensionRoute on GoRouteData { + static BigIntExtensionRoute _fromState(GoRouterState state) => + BigIntExtensionRoute( + requiredBigIntField: + BigInt.parse(state.pathParameters['requiredBigIntField']!) + as BigIntExtension, + bigIntField: + BigInt.tryParse(state.uri.queryParameters['big-int-field'] ?? '') + as BigIntExtension?, + ); + + BigIntExtensionRoute get _self => this as BigIntExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/big-int-route/${Uri.encodeComponent(_self.requiredBigIntField.toString())}', + queryParams: { + if (_self.bigIntField != null) + 'big-int-field': _self.bigIntField!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$BoolExtensionRoute on GoRouteData { + static BoolExtensionRoute _fromState(GoRouterState state) => + BoolExtensionRoute( + requiredBoolField: + bool.parse(state.pathParameters['requiredBoolField']!) + as BoolExtension, + boolField: bool.tryParse(state.uri.queryParameters['bool-field'] ?? '') + as BoolExtension?, + boolFieldWithDefaultValue: bool.tryParse( + state.uri.queryParameters['bool-field-with-default-value'] ?? + '') as BoolExtension? ?? + const BoolExtension(true), + ); + + BoolExtensionRoute get _self => this as BoolExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/bool-route/${Uri.encodeComponent(_self.requiredBoolField.toString())}', + queryParams: { + if (_self.boolField != null) + 'bool-field': _self.boolField!.toString(), + if (_self.boolFieldWithDefaultValue != const BoolExtension(true)) + 'bool-field-with-default-value': + _self.boolFieldWithDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$DateTimeExtensionRoute on GoRouteData { + static DateTimeExtensionRoute _fromState(GoRouterState state) => + DateTimeExtensionRoute( + requiredDateTimeField: + DateTime.parse(state.pathParameters['requiredDateTimeField']!) + as DateTimeExtension, + dateTimeField: DateTime.tryParse( + state.uri.queryParameters['date-time-field'] ?? '') + as DateTimeExtension?, + ); + + DateTimeExtensionRoute get _self => this as DateTimeExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/date-time-route/${Uri.encodeComponent(_self.requiredDateTimeField.toString())}', + queryParams: { + if (_self.dateTimeField != null) + 'date-time-field': _self.dateTimeField!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$DoubleExtensionRoute on GoRouteData { + static DoubleExtensionRoute _fromState(GoRouterState state) => + DoubleExtensionRoute( + requiredDoubleField: + double.parse(state.pathParameters['requiredDoubleField']!) + as DoubleExtension, + doubleField: + double.tryParse(state.uri.queryParameters['double-field'] ?? '') + as DoubleExtension?, + doubleFieldWithDefaultValue: double.tryParse( + state.uri.queryParameters['double-field-with-default-value'] ?? + '') as DoubleExtension? ?? + const DoubleExtension(1.0), + ); + + DoubleExtensionRoute get _self => this as DoubleExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/double-route/${Uri.encodeComponent(_self.requiredDoubleField.toString())}', + queryParams: { + if (_self.doubleField != null) + 'double-field': _self.doubleField!.toString(), + if (_self.doubleFieldWithDefaultValue != const DoubleExtension(1.0)) + 'double-field-with-default-value': + _self.doubleFieldWithDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$IntExtensionRoute on GoRouteData { + static IntExtensionRoute _fromState(GoRouterState state) => IntExtensionRoute( + requiredIntField: int.parse(state.pathParameters['requiredIntField']!) + as IntExtension, + intField: int.tryParse(state.uri.queryParameters['int-field'] ?? '') + as IntExtension?, + intFieldWithDefaultValue: int.tryParse( + state.uri.queryParameters['int-field-with-default-value'] ?? + '') as IntExtension? ?? + const IntExtension(1), + ); + + IntExtensionRoute get _self => this as IntExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/int-route/${Uri.encodeComponent(_self.requiredIntField.toString())}', + queryParams: { + if (_self.intField != null) 'int-field': _self.intField!.toString(), + if (_self.intFieldWithDefaultValue != const IntExtension(1)) + 'int-field-with-default-value': + _self.intFieldWithDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$NumExtensionRoute on GoRouteData { + static NumExtensionRoute _fromState(GoRouterState state) => NumExtensionRoute( + requiredNumField: num.parse(state.pathParameters['requiredNumField']!) + as NumExtension, + numField: num.tryParse(state.uri.queryParameters['num-field'] ?? '') + as NumExtension?, + numFieldWithDefaultValue: num.tryParse( + state.uri.queryParameters['num-field-with-default-value'] ?? + '') as NumExtension? ?? + const NumExtension(1), + ); + + NumExtensionRoute get _self => this as NumExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/num-route/${Uri.encodeComponent(_self.requiredNumField.toString())}', + queryParams: { + if (_self.numField != null) 'num-field': _self.numField!.toString(), + if (_self.numFieldWithDefaultValue != const NumExtension(1)) + 'num-field-with-default-value': + _self.numFieldWithDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$EnumExtensionRoute on GoRouteData { + static EnumExtensionRoute _fromState(GoRouterState state) => + EnumExtensionRoute( + requiredEnumField: _$PersonDetailsEnumMap + ._$fromName(state.pathParameters['requiredEnumField']!) + as PersonDetailsExtension, + enumField: _$PersonDetailsEnumMap._$fromName( + state.uri.queryParameters['enum-field']) as PersonDetailsExtension?, + enumFieldWithDefaultValue: _$PersonDetailsEnumMap._$fromName( + state.uri.queryParameters['enum-field-with-default-value']) + as PersonDetailsExtension? ?? + const PersonDetailsExtension(PersonDetails.favoriteFood), + ); + + EnumExtensionRoute get _self => this as EnumExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/enum-route/${Uri.encodeComponent(_$PersonDetailsEnumMap[_self.requiredEnumField as PersonDetails]!)}', + queryParams: { + if (_self.enumField != null) + 'enum-field': + _$PersonDetailsEnumMap[_self.enumField! as PersonDetails]!, + if (_self.enumFieldWithDefaultValue != + const PersonDetailsExtension(PersonDetails.favoriteFood)) + 'enum-field-with-default-value': _$PersonDetailsEnumMap[ + _self.enumFieldWithDefaultValue as PersonDetails]!, + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +const _$PersonDetailsEnumMap = { + PersonDetails.hobbies: 'hobbies', + PersonDetails.favoriteFood: 'favorite-food', + PersonDetails.favoriteSport: 'favorite-sport', +}; + +mixin _$EnhancedEnumExtensionRoute on GoRouteData { + static EnhancedEnumExtensionRoute _fromState(GoRouterState state) => + EnhancedEnumExtensionRoute( + requiredEnumField: _$SportDetailsEnumMap + ._$fromName(state.pathParameters['requiredEnumField']!) + as SportDetailsExtension, + enumField: _$SportDetailsEnumMap._$fromName( + state.uri.queryParameters['enum-field']) as SportDetailsExtension?, + enumFieldWithDefaultValue: _$SportDetailsEnumMap._$fromName( + state.uri.queryParameters['enum-field-with-default-value']) + as SportDetailsExtension? ?? + const SportDetailsExtension(SportDetails.football), + ); + + EnhancedEnumExtensionRoute get _self => this as EnhancedEnumExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/enhanced-enum-route/${Uri.encodeComponent(_$SportDetailsEnumMap[_self.requiredEnumField as SportDetails]!)}', + queryParams: { + if (_self.enumField != null) + 'enum-field': + _$SportDetailsEnumMap[_self.enumField! as SportDetails]!, + if (_self.enumFieldWithDefaultValue != + const SportDetailsExtension(SportDetails.football)) + 'enum-field-with-default-value': _$SportDetailsEnumMap[ + _self.enumFieldWithDefaultValue as SportDetails]!, + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +const _$SportDetailsEnumMap = { + SportDetails.volleyball: 'volleyball', + SportDetails.football: 'football', + SportDetails.tennis: 'tennis', + SportDetails.hockey: 'hockey', +}; + +mixin _$StringExtensionRoute on GoRouteData { + static StringExtensionRoute _fromState(GoRouterState state) => + StringExtensionRoute( + requiredStringField: + state.pathParameters['requiredStringField']! as StringExtension, + stringField: + state.uri.queryParameters['string-field'] as StringExtension?, + stringFieldWithDefaultValue: + state.uri.queryParameters['string-field-with-default-value'] + as StringExtension? ?? + const StringExtension('defaultValue'), + ); + + StringExtensionRoute get _self => this as StringExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/string-route/${Uri.encodeComponent(_self.requiredStringField as String)}', + queryParams: { + if (_self.stringField != null) + 'string-field': _self.stringField! as String, + if (_self.stringFieldWithDefaultValue != + const StringExtension('defaultValue')) + 'string-field-with-default-value': + _self.stringFieldWithDefaultValue as String, + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$UriExtensionRoute on GoRouteData { + static UriExtensionRoute _fromState(GoRouterState state) => UriExtensionRoute( + requiredUriField: Uri.parse(state.pathParameters['requiredUriField']!) + as UriExtension, + uriField: Uri.tryParse(state.uri.queryParameters['uri-field'] ?? '') + as UriExtension?, + ); + + UriExtensionRoute get _self => this as UriExtensionRoute; + + @override + String get location => GoRouteData.$location( + '/uri-route/${Uri.encodeComponent(_self.requiredUriField.toString())}', + queryParams: { + if (_self.uriField != null) 'uri-field': _self.uriField!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +extension on Map { + T? _$fromName(String? value) => + entries.where((element) => element.value == value).firstOrNull?.key; +} diff --git a/packages/go_router_builder/lib/src/route_config.dart b/packages/go_router_builder/lib/src/route_config.dart index 5a157c289fc..b15f2b3539c 100644 --- a/packages/go_router_builder/lib/src/route_config.dart +++ b/packages/go_router_builder/lib/src/route_config.dart @@ -463,6 +463,12 @@ mixin $_mixinName on GoRouteData { if (potentialEnumType.isEnum) { enumParamTypes.add(potentialEnumType as InterfaceType); } + + // Support for enum extension types + final DartType representedType = potentialEnumType.extensionTypeErasure; + if (potentialEnumType != representedType && representedType.isEnum) { + enumParamTypes.add(representedType as InterfaceType); + } } return enumParamTypes.map(_enumMapConst); } diff --git a/packages/go_router_builder/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index 4ac62d2c278..6741e21ad4f 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -43,6 +43,7 @@ const List<_TypeHelper> _helpers = <_TypeHelper>[ _TypeHelperDateTime(), _TypeHelperDouble(), _TypeHelperEnum(), + _TypeHelperExtensionType(), _TypeHelperInt(), _TypeHelperNum(), _TypeHelperString(), @@ -281,6 +282,100 @@ class _TypeHelperEnum extends _TypeHelperWithHelper { bool _matchesType(DartType type) => type.isEnum; } +/// A type helper for extension types. +/// Supported extension types are: +/// - [String] +/// - [int] +/// - [double] +/// - [num] +/// - [bool] +/// - [Enum] +/// - [BigInt] +/// - [DateTime] +/// - [Uri] +class _TypeHelperExtensionType extends _TypeHelper { + const _TypeHelperExtensionType(); + + @override + String _decode( + FormalParameterElement parameterElement, + Set pathParameters, + ) { + final DartType paramType = parameterElement.type; + if (paramType.isNullableType && parameterElement.hasDefaultValue) { + throw NullableDefaultValueError(parameterElement); + } + + final String stateValue = + 'state.${_stateValueAccess(parameterElement, pathParameters)}'; + final String castType; + if (paramType.isNullableType || parameterElement.hasDefaultValue) { + castType = '$paramType${paramType.isNullableType ? '' : '?'}'; + } else { + castType = '$paramType'; + } + + final DartType representationType = paramType.extensionTypeErasure; + if (representationType.isDartCoreString) { + return '$stateValue as $castType'; + } + + if (representationType.isEnum) { + return '${enumMapName(representationType as InterfaceType)}' + '.$enumExtensionHelperName($stateValue) as $castType'; + } + + final String representationTypeName = withoutNullability( + representationType.getDisplayString(), + ); + if (paramType.isNullableType || parameterElement.hasDefaultValue) { + return "$representationTypeName.tryParse($stateValue ?? '') as $castType"; + } else { + return '$representationTypeName.parse($stateValue) as $castType'; + } + } + + @override + String _encode(String fieldName, DartType type) { + final DartType representationType = type.extensionTypeErasure; + if (representationType.isDartCoreString) { + return '$fieldName${type.ensureNotNull} as String'; + } + + if (representationType.isEnum) { + return '${enumMapName(representationType as InterfaceType)}' + '[$fieldName${type.ensureNotNull} as ${withoutNullability(representationType.getDisplayString())}]!'; + } + + return '$fieldName${representationType.ensureNotNull}.toString()'; + } + + @override + bool _matchesType(DartType type) { + final DartType representationType = type.extensionTypeErasure; + if (type == representationType) { + // `type` is not an extension type. + return false; + } + + return representationType.isDartCoreString || + representationType.isDartCoreInt || + representationType.isDartCoreDouble || + representationType.isDartCoreNum || + representationType.isDartCoreBool || + representationType.isEnum || + const TypeChecker.fromRuntime( + BigInt, + ).isAssignableFromType(representationType) || + const TypeChecker.fromRuntime( + DateTime, + ).isAssignableFromType(representationType) || + const TypeChecker.fromRuntime( + Uri, + ).isAssignableFromType(representationType); + } +} + class _TypeHelperInt extends _TypeHelperWithHelper { const _TypeHelperInt(); diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml index acac5fed4f8..cf44bfb70a2 100644 --- a/packages/go_router_builder/pubspec.yaml +++ b/packages/go_router_builder/pubspec.yaml @@ -2,7 +2,7 @@ name: go_router_builder description: >- A builder that supports generated strongly-typed route helpers for package:go_router -version: 3.1.0 +version: 3.2.0 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_inputs/extension_type_parameter.dart b/packages/go_router_builder/test_inputs/extension_type_parameter.dart new file mode 100644 index 00000000000..976bb090fab --- /dev/null +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart @@ -0,0 +1,181 @@ +// 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:go_router/go_router.dart'; + +mixin _$ExtensionTypeParam {} +mixin _$ExtensionTypeStringParam {} +mixin _$ExtensionTypeStringDefaultParam {} +mixin _$ExtensionTypeIntParam {} +mixin _$ExtensionTypeIntDefaultParam {} +mixin _$ExtensionTypeDoubleParam {} +mixin _$ExtensionTypeNumParam {} +mixin _$ExtensionTypeBoolParam {} +mixin _$ExtensionTypeEnumType {} +mixin _$ExtensionTypeBigIntParam {} +mixin _$ExtensionTypeDateTimeParam {} +mixin _$ExtensionTypeUriType {} + +@TypedGoRoute( + path: '/', + routes: >[ + TypedGoRoute(path: 'string/:s'), + TypedGoRoute(path: 'string_default/:s'), + TypedGoRoute(path: 'int/:x'), + TypedGoRoute(path: 'int_default/:x'), + TypedGoRoute(path: 'double/:d'), + TypedGoRoute(path: 'num/:n'), + TypedGoRoute(path: 'bool/:b'), + TypedGoRoute(path: 'enum/:value'), + TypedGoRoute(path: 'bigint/:bi'), + TypedGoRoute(path: 'datetime/:dt'), + TypedGoRoute(path: 'uri/:uri'), + ], +) +class ExtensionTypeParam extends GoRouteData with _$ExtensionTypeParam { + ExtensionTypeParam(); +} + +class ExtensionTypeStringParam extends GoRouteData + with _$ExtensionTypeStringParam { + ExtensionTypeStringParam({ + required this.s, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const StringExtensionType('default'), + }); + final StringExtensionType s; + final StringExtensionType requiredValue; + final StringExtensionType? optionalNullableValue; + final StringExtensionType optionalDefaultValue; +} + +class ExtensionTypeStringDefaultParam extends GoRouteData + with _$ExtensionTypeStringDefaultParam { + ExtensionTypeStringDefaultParam({ + this.s = const StringExtensionType('default'), + }); + final StringExtensionType s; +} + +class ExtensionTypeIntParam extends GoRouteData with _$ExtensionTypeIntParam { + ExtensionTypeIntParam({ + required this.x, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const IntExtensionType(42), + }); + final IntExtensionType x; + final IntExtensionType requiredValue; + final IntExtensionType? optionalNullableValue; + final IntExtensionType optionalDefaultValue; +} + +class ExtensionTypeIntDefaultParam extends GoRouteData + with _$ExtensionTypeIntDefaultParam { + ExtensionTypeIntDefaultParam({this.x = const IntExtensionType(42)}); + final IntExtensionType x; +} + +class ExtensionTypeDoubleParam extends GoRouteData + with _$ExtensionTypeDoubleParam { + ExtensionTypeDoubleParam({ + required this.d, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const DoubleExtensionType(3.14), + }); + final DoubleExtensionType d; + final DoubleExtensionType requiredValue; + final DoubleExtensionType? optionalNullableValue; + final DoubleExtensionType optionalDefaultValue; +} + +class ExtensionTypeNumParam extends GoRouteData with _$ExtensionTypeNumParam { + ExtensionTypeNumParam({ + required this.n, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const NumExtensionType(3.14), + }); + final NumExtensionType n; + final NumExtensionType requiredValue; + final NumExtensionType? optionalNullableValue; + final NumExtensionType optionalDefaultValue; +} + +class ExtensionTypeBoolParam extends GoRouteData with _$ExtensionTypeBoolParam { + ExtensionTypeBoolParam({ + required this.b, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const BoolExtensionType(true), + }); + final BoolExtensionType b; + final BoolExtensionType requiredValue; + final BoolExtensionType? optionalNullableValue; + final BoolExtensionType optionalDefaultValue; +} + +enum MyEnum { value1, value2, value3 } + +class ExtensionTypeEnumType extends GoRouteData with _$ExtensionTypeEnumType { + ExtensionTypeEnumType({ + required this.value, + required this.requiredValue, + this.optionalNullableValue, + this.optionalDefaultValue = const EnumExtensionType(MyEnum.value1), + }); + final EnumExtensionType value; + final EnumExtensionType requiredValue; + final EnumExtensionType? optionalNullableValue; + final EnumExtensionType optionalDefaultValue; +} + +class ExtensionTypeBigIntParam extends GoRouteData + with _$ExtensionTypeBigIntParam { + ExtensionTypeBigIntParam({ + required this.bi, + required this.requiredValue, + this.optionalValue, + this.optionalNullableValue, + }); + final BigIntExtensionType bi; + final BigIntExtensionType requiredValue; + final BigIntExtensionType? optionalValue; + final BigIntExtensionType? optionalNullableValue; +} + +class ExtensionTypeDateTimeParam extends GoRouteData + with _$ExtensionTypeDateTimeParam { + ExtensionTypeDateTimeParam({ + required this.dt, + required this.optionalValue, + this.optionalNullableValue, + }); + final DateTimeExtensionType dt; + final DateTimeExtensionType optionalValue; + final DateTimeExtensionType? optionalNullableValue; +} + +class ExtensionTypeUriType extends GoRouteData with _$ExtensionTypeUriType { + ExtensionTypeUriType({ + required this.uri, + required this.requiredValue, + this.optionalNullableValue, + }); + final UriExtensionType uri; + final UriExtensionType requiredValue; + final UriExtensionType? optionalNullableValue; +} + +extension type const StringExtensionType(String value) {} +extension type const IntExtensionType(int value) {} +extension type const DoubleExtensionType(double value) {} +extension type const NumExtensionType(num value) {} +extension type const BoolExtensionType(bool value) {} +extension type const EnumExtensionType(MyEnum value) {} +extension type const BigIntExtensionType(BigInt value) {} +extension type const DateTimeExtensionType(DateTime value) {} +extension type const UriExtensionType(Uri value) {} diff --git a/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect new file mode 100644 index 00000000000..d0e7638af9d --- /dev/null +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect @@ -0,0 +1,524 @@ +RouteBase get $extensionTypeParam => GoRouteData.$route( + path: '/', + factory: _$ExtensionTypeParam._fromState, + routes: [ + GoRouteData.$route( + path: 'string/:s', + factory: _$ExtensionTypeStringParam._fromState, + ), + GoRouteData.$route( + path: 'string_default/:s', + factory: _$ExtensionTypeStringDefaultParam._fromState, + ), + GoRouteData.$route( + path: 'int/:x', + factory: _$ExtensionTypeIntParam._fromState, + ), + GoRouteData.$route( + path: 'int_default/:x', + factory: _$ExtensionTypeIntDefaultParam._fromState, + ), + GoRouteData.$route( + path: 'double/:d', + factory: _$ExtensionTypeDoubleParam._fromState, + ), + GoRouteData.$route( + path: 'num/:n', + factory: _$ExtensionTypeNumParam._fromState, + ), + GoRouteData.$route( + path: 'bool/:b', + factory: _$ExtensionTypeBoolParam._fromState, + ), + GoRouteData.$route( + path: 'enum/:value', + factory: _$ExtensionTypeEnumType._fromState, + ), + GoRouteData.$route( + path: 'bigint/:bi', + factory: _$ExtensionTypeBigIntParam._fromState, + ), + GoRouteData.$route( + path: 'datetime/:dt', + factory: _$ExtensionTypeDateTimeParam._fromState, + ), + GoRouteData.$route( + path: 'uri/:uri', + factory: _$ExtensionTypeUriType._fromState, + ), + ], + ); + +mixin _$ExtensionTypeParam on GoRouteData { + static ExtensionTypeParam _fromState(GoRouterState state) => + ExtensionTypeParam(); + + @override + String get location => GoRouteData.$location( + '/', + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeStringParam on GoRouteData { + static ExtensionTypeStringParam _fromState(GoRouterState state) => + ExtensionTypeStringParam( + s: state.pathParameters['s']! as StringExtensionType, + requiredValue: + state.uri.queryParameters['required-value']! as StringExtensionType, + optionalNullableValue: state.uri + .queryParameters['optional-nullable-value'] as StringExtensionType?, + optionalDefaultValue: + state.uri.queryParameters['optional-default-value'] + as StringExtensionType? ?? + const StringExtensionType('default'), + ); + + ExtensionTypeStringParam get _self => this as ExtensionTypeStringParam; + + @override + String get location => GoRouteData.$location( + '/string/${Uri.encodeComponent(_self.s as String)}', + queryParams: { + 'required-value': _self.requiredValue as String, + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue! as String, + if (_self.optionalDefaultValue != + const StringExtensionType('default')) + 'optional-default-value': _self.optionalDefaultValue as String, + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeStringDefaultParam on GoRouteData { + static ExtensionTypeStringDefaultParam _fromState(GoRouterState state) => + ExtensionTypeStringDefaultParam( + s: state.pathParameters['s'] as StringExtensionType? ?? + const StringExtensionType('default'), + ); + + ExtensionTypeStringDefaultParam get _self => + this as ExtensionTypeStringDefaultParam; + + @override + String get location => GoRouteData.$location( + '/string_default/${Uri.encodeComponent(_self.s as String)}', + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeIntParam on GoRouteData { + static ExtensionTypeIntParam _fromState(GoRouterState state) => + ExtensionTypeIntParam( + x: int.parse(state.pathParameters['x']!) as IntExtensionType, + requiredValue: int.parse(state.uri.queryParameters['required-value']!) + as IntExtensionType, + optionalNullableValue: int.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as IntExtensionType?, + optionalDefaultValue: int.tryParse( + state.uri.queryParameters['optional-default-value'] ?? '') + as IntExtensionType? ?? + const IntExtensionType(42), + ); + + ExtensionTypeIntParam get _self => this as ExtensionTypeIntParam; + + @override + String get location => GoRouteData.$location( + '/int/${Uri.encodeComponent(_self.x.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + if (_self.optionalDefaultValue != const IntExtensionType(42)) + 'optional-default-value': _self.optionalDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeIntDefaultParam on GoRouteData { + static ExtensionTypeIntDefaultParam _fromState(GoRouterState state) => + ExtensionTypeIntDefaultParam( + x: int.tryParse(state.pathParameters['x'] ?? '') as IntExtensionType? ?? + const IntExtensionType(42), + ); + + ExtensionTypeIntDefaultParam get _self => + this as ExtensionTypeIntDefaultParam; + + @override + String get location => GoRouteData.$location( + '/int_default/${Uri.encodeComponent(_self.x.toString())}', + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeDoubleParam on GoRouteData { + static ExtensionTypeDoubleParam _fromState(GoRouterState state) => + ExtensionTypeDoubleParam( + d: double.parse(state.pathParameters['d']!) as DoubleExtensionType, + requiredValue: + double.parse(state.uri.queryParameters['required-value']!) + as DoubleExtensionType, + optionalNullableValue: double.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as DoubleExtensionType?, + optionalDefaultValue: double.tryParse( + state.uri.queryParameters['optional-default-value'] ?? '') + as DoubleExtensionType? ?? + const DoubleExtensionType(3.14), + ); + + ExtensionTypeDoubleParam get _self => this as ExtensionTypeDoubleParam; + + @override + String get location => GoRouteData.$location( + '/double/${Uri.encodeComponent(_self.d.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + if (_self.optionalDefaultValue != const DoubleExtensionType(3.14)) + 'optional-default-value': _self.optionalDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeNumParam on GoRouteData { + static ExtensionTypeNumParam _fromState(GoRouterState state) => + ExtensionTypeNumParam( + n: num.parse(state.pathParameters['n']!) as NumExtensionType, + requiredValue: num.parse(state.uri.queryParameters['required-value']!) + as NumExtensionType, + optionalNullableValue: num.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as NumExtensionType?, + optionalDefaultValue: num.tryParse( + state.uri.queryParameters['optional-default-value'] ?? '') + as NumExtensionType? ?? + const NumExtensionType(3.14), + ); + + ExtensionTypeNumParam get _self => this as ExtensionTypeNumParam; + + @override + String get location => GoRouteData.$location( + '/num/${Uri.encodeComponent(_self.n.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + if (_self.optionalDefaultValue != const NumExtensionType(3.14)) + 'optional-default-value': _self.optionalDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeBoolParam on GoRouteData { + static ExtensionTypeBoolParam _fromState(GoRouterState state) => + ExtensionTypeBoolParam( + b: bool.parse(state.pathParameters['b']!) as BoolExtensionType, + requiredValue: bool.parse(state.uri.queryParameters['required-value']!) + as BoolExtensionType, + optionalNullableValue: bool.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as BoolExtensionType?, + optionalDefaultValue: bool.tryParse( + state.uri.queryParameters['optional-default-value'] ?? '') + as BoolExtensionType? ?? + const BoolExtensionType(true), + ); + + ExtensionTypeBoolParam get _self => this as ExtensionTypeBoolParam; + + @override + String get location => GoRouteData.$location( + '/bool/${Uri.encodeComponent(_self.b.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + if (_self.optionalDefaultValue != const BoolExtensionType(true)) + 'optional-default-value': _self.optionalDefaultValue.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeEnumType on GoRouteData { + static ExtensionTypeEnumType _fromState(GoRouterState state) => + ExtensionTypeEnumType( + value: _$MyEnumEnumMap._$fromName(state.pathParameters['value']!) + as EnumExtensionType, + requiredValue: _$MyEnumEnumMap._$fromName( + state.uri.queryParameters['required-value']!) as EnumExtensionType, + optionalNullableValue: _$MyEnumEnumMap._$fromName( + state.uri.queryParameters['optional-nullable-value']) + as EnumExtensionType?, + optionalDefaultValue: _$MyEnumEnumMap._$fromName( + state.uri.queryParameters['optional-default-value']) + as EnumExtensionType? ?? + const EnumExtensionType(MyEnum.value1), + ); + + ExtensionTypeEnumType get _self => this as ExtensionTypeEnumType; + + @override + String get location => GoRouteData.$location( + '/enum/${Uri.encodeComponent(_$MyEnumEnumMap[_self.value as MyEnum]!)}', + queryParams: { + 'required-value': _$MyEnumEnumMap[_self.requiredValue as MyEnum]!, + if (_self.optionalNullableValue != null) + 'optional-nullable-value': + _$MyEnumEnumMap[_self.optionalNullableValue! as MyEnum]!, + if (_self.optionalDefaultValue != + const EnumExtensionType(MyEnum.value1)) + 'optional-default-value': + _$MyEnumEnumMap[_self.optionalDefaultValue as MyEnum]!, + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +const _$MyEnumEnumMap = { + MyEnum.value1: 'value1', + MyEnum.value2: 'value2', + MyEnum.value3: 'value3', +}; + +mixin _$ExtensionTypeBigIntParam on GoRouteData { + static ExtensionTypeBigIntParam _fromState(GoRouterState state) => + ExtensionTypeBigIntParam( + bi: BigInt.parse(state.pathParameters['bi']!) as BigIntExtensionType, + requiredValue: + BigInt.parse(state.uri.queryParameters['required-value']!) + as BigIntExtensionType, + optionalValue: + BigInt.tryParse(state.uri.queryParameters['optional-value'] ?? '') + as BigIntExtensionType?, + optionalNullableValue: BigInt.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as BigIntExtensionType?, + ); + + ExtensionTypeBigIntParam get _self => this as ExtensionTypeBigIntParam; + + @override + String get location => GoRouteData.$location( + '/bigint/${Uri.encodeComponent(_self.bi.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalValue != null) + 'optional-value': _self.optionalValue!.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeDateTimeParam on GoRouteData { + static ExtensionTypeDateTimeParam _fromState(GoRouterState state) => + ExtensionTypeDateTimeParam( + dt: DateTime.parse(state.pathParameters['dt']!) + as DateTimeExtensionType, + optionalValue: + DateTime.parse(state.uri.queryParameters['optional-value']!) + as DateTimeExtensionType, + optionalNullableValue: DateTime.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as DateTimeExtensionType?, + ); + + ExtensionTypeDateTimeParam get _self => this as ExtensionTypeDateTimeParam; + + @override + String get location => GoRouteData.$location( + '/datetime/${Uri.encodeComponent(_self.dt.toString())}', + queryParams: { + 'optional-value': _self.optionalValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +mixin _$ExtensionTypeUriType on GoRouteData { + static ExtensionTypeUriType _fromState(GoRouterState state) => + ExtensionTypeUriType( + uri: Uri.parse(state.pathParameters['uri']!) as UriExtensionType, + requiredValue: Uri.parse(state.uri.queryParameters['required-value']!) + as UriExtensionType, + optionalNullableValue: Uri.tryParse( + state.uri.queryParameters['optional-nullable-value'] ?? '') + as UriExtensionType?, + ); + + ExtensionTypeUriType get _self => this as ExtensionTypeUriType; + + @override + String get location => GoRouteData.$location( + '/uri/${Uri.encodeComponent(_self.uri.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue!.toString(), + }, + ); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + +extension on Map { + T? _$fromName(String? value) => + entries.where((element) => element.value == value).firstOrNull?.key; +} \ No newline at end of file