From 688ec244a380ea3fb13894e19a76f4f290fbaede Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Fri, 20 Jun 2025 08:37:05 +0900 Subject: [PATCH 01/14] feat: Support extension type --- packages/go_router_builder/README.md | 2 +- .../lib/src/type_helpers.dart | 44 ++ .../test_inputs/extension_type_parameter.dart | 169 ++++++ .../extension_type_parameter.dart.expect | 485 ++++++++++++++++++ 4 files changed, 699 insertions(+), 1 deletion(-) create mode 100644 packages/go_router_builder/test_inputs/extension_type_parameter.dart create mode 100644 packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect diff --git a/packages/go_router_builder/README.md b/packages/go_router_builder/README.md index 91545bda047..7e8c430f041 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/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index 5f809c335d3..925bbbfa49a 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(), @@ -263,6 +264,49 @@ class _TypeHelperEnum extends _TypeHelperWithHelper { bool _matchesType(DartType type) => type.isEnum; } +/// A type helper for extension types. +class _TypeHelperExtensionType extends _TypeHelper { + const _TypeHelperExtensionType(); + + @override + String _decode( + ParameterElement 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 extensionTypeErasure = paramType.extensionTypeErasure; + if (extensionTypeErasure.isDartCoreString) { + return '$stateValue as $castType'; + } + + final String parseTypeName = + withoutNullability(extensionTypeErasure.getDisplayString()); + if (paramType.isNullableType || parameterElement.hasDefaultValue) { + return "$parseTypeName.tryParse($stateValue ?? '') as $castType"; + } else { + return '$parseTypeName.parse($stateValue) as $castType'; + } + } + + @override + String _encode(String fieldName, DartType type) => + '$fieldName${type.isNullableType ? '?' : ''}.toString()'; + + @override + bool _matchesType(DartType type) => type != type.extensionTypeErasure; +} + class _TypeHelperInt extends _TypeHelperWithHelper { const _TypeHelperInt(); 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..27b13ea6e0a --- /dev/null +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart @@ -0,0 +1,169 @@ +// 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 _$ExtenstionTypeParam {} +mixin _$ExtenstionTypeStringParam {} +mixin _$ExtenstionTypeStringOptionalParam {} +mixin _$ExtenstionTypeStringDefaultParam {} +mixin _$ExtenstionTypeIntParam {} +mixin _$ExtenstionTypeIntOptionalParam {} +mixin _$ExtenstionTypeIntDefaultParam {} +mixin _$ExtenstionTypeDoubleParam {} +mixin _$ExtenstionTypeNumParam {} +mixin _$ExtenstionTypeBoolParam {} +mixin _$ExtenstionTypeBigIntParam {} +mixin _$ExtenstionTypeDateTimeParam {} + +@TypedGoRoute(path: '/', routes: >[ + TypedGoRoute(path: 'string/:s'), + TypedGoRoute(path: 'string_optional/:s'), + TypedGoRoute(path: 'string_default/:s'), + TypedGoRoute(path: 'int/:x'), + TypedGoRoute(path: 'int_optional/:x'), + TypedGoRoute(path: 'int_default/:x'), + TypedGoRoute(path: 'double/:d'), + TypedGoRoute(path: 'num/:n'), + TypedGoRoute(path: 'bool/:b'), + TypedGoRoute(path: 'bigint/:bi'), + TypedGoRoute(path: 'datetime/:dt'), +]) +class ExtenstionTypeParam extends GoRouteData with _$ExtenstionTypeParam { + ExtenstionTypeParam(); +} + +class ExtenstionTypeStringParam extends GoRouteData + with _$ExtenstionTypeStringParam { + ExtenstionTypeStringParam({ + 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 ExtenstionTypeStringOptionalParam extends GoRouteData + with _$ExtenstionTypeStringOptionalParam { + ExtenstionTypeStringOptionalParam({ + this.s, + }); + final StringExtensionType? s; +} + +class ExtenstionTypeStringDefaultParam extends GoRouteData + with _$ExtenstionTypeStringDefaultParam { + ExtenstionTypeStringDefaultParam({ + this.s = const StringExtensionType('default'), + }); + final StringExtensionType s; +} + +class ExtenstionTypeIntParam extends GoRouteData with _$ExtenstionTypeIntParam { + ExtenstionTypeIntParam({ + 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 ExtenstionTypeIntOptionalParam extends GoRouteData + with _$ExtenstionTypeIntOptionalParam { + ExtenstionTypeIntOptionalParam({ + this.x, + }); + final IntExtensionType? x; +} + +class ExtenstionTypeIntDefaultParam extends GoRouteData + with _$ExtenstionTypeIntDefaultParam { + ExtenstionTypeIntDefaultParam({ + this.x = const IntExtensionType(42), + }); + final IntExtensionType x; +} + +class ExtenstionTypeDoubleParam extends GoRouteData + with _$ExtenstionTypeDoubleParam { + ExtenstionTypeDoubleParam({ + 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 ExtenstionTypeNumParam extends GoRouteData with _$ExtenstionTypeNumParam { + ExtenstionTypeNumParam({ + 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 ExtenstionTypeBoolParam extends GoRouteData + with _$ExtenstionTypeBoolParam { + ExtenstionTypeBoolParam({ + 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; +} + +class ExtenstionTypeBigIntParam extends GoRouteData + with _$ExtenstionTypeBigIntParam { + ExtenstionTypeBigIntParam({ + required this.bi, + required this.requiredValue, + this.optionalValue, + this.optionalNullableValue, + }); + final BigIntExtensionType bi; + final BigIntExtensionType requiredValue; + final BigIntExtensionType? optionalValue; + final BigIntExtensionType? optionalNullableValue; +} + +class ExtenstionTypeDateTimeParam extends GoRouteData + with _$ExtenstionTypeDateTimeParam { + ExtenstionTypeDateTimeParam({ + required this.dt, + required this.optionalValue, + this.optionalNullableValue, + }); + final DateTimeExtensionType dt; + final DateTimeExtensionType optionalValue; + final DateTimeExtensionType? optionalNullableValue; +} + +extension type const StringExtensionType(String value) {} +extension type const NumExtensionType(num value) {} +extension type const IntExtensionType(int value) {} +extension type const DoubleExtensionType(double value) {} +extension type const BoolExtensionType(bool value) {} +extension type const BigIntExtensionType(BigInt value) {} +extension type const DateTimeExtensionType(DateTime 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..2f048c55c56 --- /dev/null +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect @@ -0,0 +1,485 @@ +RouteBase get $extenstionTypeParam => GoRouteData.$route( + path: '/', + factory: _$ExtenstionTypeParam._fromState, + routes: [ + GoRouteData.$route( + path: 'string/:s', + factory: _$ExtenstionTypeStringParam._fromState, + ), + GoRouteData.$route( + path: 'string_optional/:s', + factory: _$ExtenstionTypeStringOptionalParam._fromState, + ), + GoRouteData.$route( + path: 'string_default/:s', + factory: _$ExtenstionTypeStringDefaultParam._fromState, + ), + GoRouteData.$route( + path: 'int/:x', + factory: _$ExtenstionTypeIntParam._fromState, + ), + GoRouteData.$route( + path: 'int_optional/:x', + factory: _$ExtenstionTypeIntOptionalParam._fromState, + ), + GoRouteData.$route( + path: 'int_default/:x', + factory: _$ExtenstionTypeIntDefaultParam._fromState, + ), + GoRouteData.$route( + path: 'double/:d', + factory: _$ExtenstionTypeDoubleParam._fromState, + ), + GoRouteData.$route( + path: 'num/:n', + factory: _$ExtenstionTypeNumParam._fromState, + ), + GoRouteData.$route( + path: 'bool/:b', + factory: _$ExtenstionTypeBoolParam._fromState, + ), + GoRouteData.$route( + path: 'bigint/:bi', + factory: _$ExtenstionTypeBigIntParam._fromState, + ), + GoRouteData.$route( + path: 'datetime/:dt', + factory: _$ExtenstionTypeDateTimeParam._fromState, + ), + ], + ); + +mixin _$ExtenstionTypeParam on GoRouteData { + static ExtenstionTypeParam _fromState(GoRouterState state) => + ExtenstionTypeParam(); + + @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 _$ExtenstionTypeStringParam on GoRouteData { + static ExtenstionTypeStringParam _fromState(GoRouterState state) => + ExtenstionTypeStringParam( + 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'), + ); + + ExtenstionTypeStringParam get _self => this as ExtenstionTypeStringParam; + + @override + String get location => GoRouteData.$location( + '/string/${Uri.encodeComponent(_self.s.toString())}', + queryParams: { + 'required-value': _self.requiredValue.toString(), + if (_self.optionalNullableValue != null) + 'optional-nullable-value': _self.optionalNullableValue?.toString(), + if (_self.optionalDefaultValue != + const StringExtensionType('default')) + '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 _$ExtenstionTypeStringOptionalParam on GoRouteData { + static ExtenstionTypeStringOptionalParam _fromState(GoRouterState state) => + ExtenstionTypeStringOptionalParam( + s: state.pathParameters['s'] as StringExtensionType?, + ); + + ExtenstionTypeStringOptionalParam get _self => + this as ExtenstionTypeStringOptionalParam; + + @override + String get location => GoRouteData.$location( + '/string_optional/${Uri.encodeComponent(_self.s?.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 _$ExtenstionTypeStringDefaultParam on GoRouteData { + static ExtenstionTypeStringDefaultParam _fromState(GoRouterState state) => + ExtenstionTypeStringDefaultParam( + s: state.pathParameters['s'] as StringExtensionType? ?? + const StringExtensionType('default'), + ); + + ExtenstionTypeStringDefaultParam get _self => + this as ExtenstionTypeStringDefaultParam; + + @override + String get location => GoRouteData.$location( + '/string_default/${Uri.encodeComponent(_self.s.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 _$ExtenstionTypeIntParam on GoRouteData { + static ExtenstionTypeIntParam _fromState(GoRouterState state) => + ExtenstionTypeIntParam( + 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), + ); + + ExtenstionTypeIntParam get _self => this as ExtenstionTypeIntParam; + + @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 _$ExtenstionTypeIntOptionalParam on GoRouteData { + static ExtenstionTypeIntOptionalParam _fromState(GoRouterState state) => + ExtenstionTypeIntOptionalParam( + x: int.tryParse(state.pathParameters['x'] ?? '') as IntExtensionType?, + ); + + ExtenstionTypeIntOptionalParam get _self => + this as ExtenstionTypeIntOptionalParam; + + @override + String get location => GoRouteData.$location( + '/int_optional/${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 _$ExtenstionTypeIntDefaultParam on GoRouteData { + static ExtenstionTypeIntDefaultParam _fromState(GoRouterState state) => + ExtenstionTypeIntDefaultParam( + x: int.tryParse(state.pathParameters['x'] ?? '') as IntExtensionType? ?? + const IntExtensionType(42), + ); + + ExtenstionTypeIntDefaultParam get _self => + this as ExtenstionTypeIntDefaultParam; + + @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 _$ExtenstionTypeDoubleParam on GoRouteData { + static ExtenstionTypeDoubleParam _fromState(GoRouterState state) => + ExtenstionTypeDoubleParam( + 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), + ); + + ExtenstionTypeDoubleParam get _self => this as ExtenstionTypeDoubleParam; + + @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 _$ExtenstionTypeNumParam on GoRouteData { + static ExtenstionTypeNumParam _fromState(GoRouterState state) => + ExtenstionTypeNumParam( + 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), + ); + + ExtenstionTypeNumParam get _self => this as ExtenstionTypeNumParam; + + @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 _$ExtenstionTypeBoolParam on GoRouteData { + static ExtenstionTypeBoolParam _fromState(GoRouterState state) => + ExtenstionTypeBoolParam( + 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), + ); + + ExtenstionTypeBoolParam get _self => this as ExtenstionTypeBoolParam; + + @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 _$ExtenstionTypeBigIntParam on GoRouteData { + static ExtenstionTypeBigIntParam _fromState(GoRouterState state) => + ExtenstionTypeBigIntParam( + 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?, + ); + + ExtenstionTypeBigIntParam get _self => this as ExtenstionTypeBigIntParam; + + @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 _$ExtenstionTypeDateTimeParam on GoRouteData { + static ExtenstionTypeDateTimeParam _fromState(GoRouterState state) => + ExtenstionTypeDateTimeParam( + 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?, + ); + + ExtenstionTypeDateTimeParam get _self => this as ExtenstionTypeDateTimeParam; + + @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); +} From bf334dc57de03cf0e9ae01d97ce77bd7db241882 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Fri, 20 Jun 2025 09:09:26 +0900 Subject: [PATCH 02/14] chore: 3.0.2 --- packages/go_router_builder/CHANGELOG.md | 3 ++- packages/go_router_builder/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index b9ef60077e8..19f3072cdb5 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.0.2 - Restricts `build` to versions less than 2.5.0. +- Support `extension type`. ## 3.0.1 diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml index 7d7dde0c893..032a8a74bad 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.0.1 +version: 3.0.2 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 From 8cf27d2accfc5ac883745f6aeca15454201054a3 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Sun, 22 Jun 2025 06:46:55 +0900 Subject: [PATCH 03/14] test: Remove nullable path case --- .../test_inputs/extension_type_parameter.dart | 22 +------ .../extension_type_parameter.dart.expect | 64 ------------------- 2 files changed, 1 insertion(+), 85 deletions(-) diff --git a/packages/go_router_builder/test_inputs/extension_type_parameter.dart b/packages/go_router_builder/test_inputs/extension_type_parameter.dart index 27b13ea6e0a..c7d154e668c 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart @@ -6,10 +6,8 @@ import 'package:go_router/go_router.dart'; mixin _$ExtenstionTypeParam {} mixin _$ExtenstionTypeStringParam {} -mixin _$ExtenstionTypeStringOptionalParam {} mixin _$ExtenstionTypeStringDefaultParam {} mixin _$ExtenstionTypeIntParam {} -mixin _$ExtenstionTypeIntOptionalParam {} mixin _$ExtenstionTypeIntDefaultParam {} mixin _$ExtenstionTypeDoubleParam {} mixin _$ExtenstionTypeNumParam {} @@ -19,10 +17,8 @@ mixin _$ExtenstionTypeDateTimeParam {} @TypedGoRoute(path: '/', routes: >[ TypedGoRoute(path: 'string/:s'), - TypedGoRoute(path: 'string_optional/:s'), TypedGoRoute(path: 'string_default/:s'), TypedGoRoute(path: 'int/:x'), - TypedGoRoute(path: 'int_optional/:x'), TypedGoRoute(path: 'int_default/:x'), TypedGoRoute(path: 'double/:d'), TypedGoRoute(path: 'num/:n'), @@ -48,14 +44,6 @@ class ExtenstionTypeStringParam extends GoRouteData final StringExtensionType optionalDefaultValue; } -class ExtenstionTypeStringOptionalParam extends GoRouteData - with _$ExtenstionTypeStringOptionalParam { - ExtenstionTypeStringOptionalParam({ - this.s, - }); - final StringExtensionType? s; -} - class ExtenstionTypeStringDefaultParam extends GoRouteData with _$ExtenstionTypeStringDefaultParam { ExtenstionTypeStringDefaultParam({ @@ -77,14 +65,6 @@ class ExtenstionTypeIntParam extends GoRouteData with _$ExtenstionTypeIntParam { final IntExtensionType optionalDefaultValue; } -class ExtenstionTypeIntOptionalParam extends GoRouteData - with _$ExtenstionTypeIntOptionalParam { - ExtenstionTypeIntOptionalParam({ - this.x, - }); - final IntExtensionType? x; -} - class ExtenstionTypeIntDefaultParam extends GoRouteData with _$ExtenstionTypeIntDefaultParam { ExtenstionTypeIntDefaultParam({ @@ -161,9 +141,9 @@ class ExtenstionTypeDateTimeParam extends GoRouteData } extension type const StringExtensionType(String value) {} -extension type const NumExtensionType(num 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 BigIntExtensionType(BigInt value) {} extension type const DateTimeExtensionType(DateTime 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 index 2f048c55c56..7fad812d761 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect @@ -6,10 +6,6 @@ RouteBase get $extenstionTypeParam => GoRouteData.$route( path: 'string/:s', factory: _$ExtenstionTypeStringParam._fromState, ), - GoRouteData.$route( - path: 'string_optional/:s', - factory: _$ExtenstionTypeStringOptionalParam._fromState, - ), GoRouteData.$route( path: 'string_default/:s', factory: _$ExtenstionTypeStringDefaultParam._fromState, @@ -18,10 +14,6 @@ RouteBase get $extenstionTypeParam => GoRouteData.$route( path: 'int/:x', factory: _$ExtenstionTypeIntParam._fromState, ), - GoRouteData.$route( - path: 'int_optional/:x', - factory: _$ExtenstionTypeIntOptionalParam._fromState, - ), GoRouteData.$route( path: 'int_default/:x', factory: _$ExtenstionTypeIntDefaultParam._fromState, @@ -115,34 +107,6 @@ mixin _$ExtenstionTypeStringParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeStringOptionalParam on GoRouteData { - static ExtenstionTypeStringOptionalParam _fromState(GoRouterState state) => - ExtenstionTypeStringOptionalParam( - s: state.pathParameters['s'] as StringExtensionType?, - ); - - ExtenstionTypeStringOptionalParam get _self => - this as ExtenstionTypeStringOptionalParam; - - @override - String get location => GoRouteData.$location( - '/string_optional/${Uri.encodeComponent(_self.s?.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 _$ExtenstionTypeStringDefaultParam on GoRouteData { static ExtenstionTypeStringDefaultParam _fromState(GoRouterState state) => ExtenstionTypeStringDefaultParam( @@ -215,34 +179,6 @@ mixin _$ExtenstionTypeIntParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeIntOptionalParam on GoRouteData { - static ExtenstionTypeIntOptionalParam _fromState(GoRouterState state) => - ExtenstionTypeIntOptionalParam( - x: int.tryParse(state.pathParameters['x'] ?? '') as IntExtensionType?, - ); - - ExtenstionTypeIntOptionalParam get _self => - this as ExtenstionTypeIntOptionalParam; - - @override - String get location => GoRouteData.$location( - '/int_optional/${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 _$ExtenstionTypeIntDefaultParam on GoRouteData { static ExtenstionTypeIntDefaultParam _fromState(GoRouterState state) => ExtenstionTypeIntDefaultParam( From af33a0b06b747189b6d18c7a151f2bce591d62b4 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Sun, 22 Jun 2025 06:54:20 +0900 Subject: [PATCH 04/14] refactor: Fix encode case --- .../go_router_builder/lib/src/type_helpers.dart | 2 +- .../extension_type_parameter.dart.expect | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/go_router_builder/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index 925bbbfa49a..72a40088f3b 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -301,7 +301,7 @@ class _TypeHelperExtensionType extends _TypeHelper { @override String _encode(String fieldName, DartType type) => - '$fieldName${type.isNullableType ? '?' : ''}.toString()'; + '$fieldName${type.ensureNotNull}.toString()'; @override bool _matchesType(DartType type) => type != type.extensionTypeErasure; 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 index 7fad812d761..aa8ad0054c8 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect @@ -86,7 +86,7 @@ mixin _$ExtenstionTypeStringParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), if (_self.optionalDefaultValue != const StringExtensionType('default')) 'optional-default-value': _self.optionalDefaultValue.toString(), @@ -159,7 +159,7 @@ mixin _$ExtenstionTypeIntParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), if (_self.optionalDefaultValue != const IntExtensionType(42)) 'optional-default-value': _self.optionalDefaultValue.toString(), }, @@ -232,7 +232,7 @@ mixin _$ExtenstionTypeDoubleParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), if (_self.optionalDefaultValue != const DoubleExtensionType(3.14)) 'optional-default-value': _self.optionalDefaultValue.toString(), }, @@ -275,7 +275,7 @@ mixin _$ExtenstionTypeNumParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), if (_self.optionalDefaultValue != const NumExtensionType(3.14)) 'optional-default-value': _self.optionalDefaultValue.toString(), }, @@ -318,7 +318,7 @@ mixin _$ExtenstionTypeBoolParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), if (_self.optionalDefaultValue != const BoolExtensionType(true)) 'optional-default-value': _self.optionalDefaultValue.toString(), }, @@ -361,9 +361,9 @@ mixin _$ExtenstionTypeBigIntParam on GoRouteData { queryParams: { 'required-value': _self.requiredValue.toString(), if (_self.optionalValue != null) - 'optional-value': _self.optionalValue?.toString(), + 'optional-value': _self.optionalValue!.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), }, ); @@ -402,7 +402,7 @@ mixin _$ExtenstionTypeDateTimeParam on GoRouteData { queryParams: { 'optional-value': _self.optionalValue.toString(), if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue?.toString(), + 'optional-nullable-value': _self.optionalNullableValue!.toString(), }, ); From 61901a349e48ebe5155f3bbde768844bd7c16d5d Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Tue, 24 Jun 2025 20:43:12 +0900 Subject: [PATCH 05/14] feat: Clarify which extension types are supported --- .../lib/src/route_config.dart | 6 ++ .../lib/src/type_helpers.dart | 63 ++++++++++++++++--- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/packages/go_router_builder/lib/src/route_config.dart b/packages/go_router_builder/lib/src/route_config.dart index d95663d15ac..b188e943228 100644 --- a/packages/go_router_builder/lib/src/route_config.dart +++ b/packages/go_router_builder/lib/src/route_config.dart @@ -457,6 +457,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 72a40088f3b..732fb612509 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -265,6 +265,16 @@ class _TypeHelperEnum extends _TypeHelperWithHelper { } /// A type helper for extension types. +/// Suppoted extension types are: +/// - [String] +/// - [int] +/// - [double] +/// - [num] +/// - [bool] +/// - [Enum] +/// - [BigInt] +/// - [DateTime] +/// - [Uri] class _TypeHelperExtensionType extends _TypeHelper { const _TypeHelperExtensionType(); @@ -285,26 +295,61 @@ class _TypeHelperExtensionType extends _TypeHelper { castType = '$paramType'; } - final DartType extensionTypeErasure = paramType.extensionTypeErasure; - if (extensionTypeErasure.isDartCoreString) { + final DartType representationType = paramType.extensionTypeErasure; + if (representationType.isDartCoreString) { return '$stateValue as $castType'; } - final String parseTypeName = - withoutNullability(extensionTypeErasure.getDisplayString()); + if (representationType.isEnum) { + return '${enumMapName(representationType as InterfaceType)}' + '.$enumExtensionHelperName($stateValue) as $castType'; + } + + final String representationTypeName = + withoutNullability(representationType.getDisplayString()); if (paramType.isNullableType || parameterElement.hasDefaultValue) { - return "$parseTypeName.tryParse($stateValue ?? '') as $castType"; + return "$representationTypeName.tryParse($stateValue ?? '') as $castType"; } else { - return '$parseTypeName.parse($stateValue) as $castType'; + return '$representationTypeName.parse($stateValue) as $castType'; } } @override - String _encode(String fieldName, DartType type) => - '$fieldName${type.ensureNotNull}.toString()'; + 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) => type != type.extensionTypeErasure; + 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 { From 53e30bb3b6fed4a1c6fe8381956c4606d9262d8e Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Tue, 24 Jun 2025 20:44:19 +0900 Subject: [PATCH 06/14] test: Update test cases --- .../test_inputs/extension_type_parameter.dart | 127 ++++++---- .../extension_type_parameter.dart.expect | 217 +++++++++++++----- 2 files changed, 239 insertions(+), 105 deletions(-) diff --git a/packages/go_router_builder/test_inputs/extension_type_parameter.dart b/packages/go_router_builder/test_inputs/extension_type_parameter.dart index c7d154e668c..04b6b4ae37e 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart @@ -4,35 +4,39 @@ import 'package:go_router/go_router.dart'; -mixin _$ExtenstionTypeParam {} -mixin _$ExtenstionTypeStringParam {} -mixin _$ExtenstionTypeStringDefaultParam {} -mixin _$ExtenstionTypeIntParam {} -mixin _$ExtenstionTypeIntDefaultParam {} -mixin _$ExtenstionTypeDoubleParam {} -mixin _$ExtenstionTypeNumParam {} -mixin _$ExtenstionTypeBoolParam {} -mixin _$ExtenstionTypeBigIntParam {} -mixin _$ExtenstionTypeDateTimeParam {} - -@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: 'bigint/:bi'), - TypedGoRoute(path: 'datetime/:dt'), +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 ExtenstionTypeParam extends GoRouteData with _$ExtenstionTypeParam { - ExtenstionTypeParam(); +class ExtensionTypeParam extends GoRouteData with _$ExtensionTypeParam { + ExtensionTypeParam(); } -class ExtenstionTypeStringParam extends GoRouteData - with _$ExtenstionTypeStringParam { - ExtenstionTypeStringParam({ +class ExtensionTypeStringParam extends GoRouteData + with _$ExtensionTypeStringParam { + ExtensionTypeStringParam({ required this.s, required this.requiredValue, this.optionalNullableValue, @@ -44,16 +48,16 @@ class ExtenstionTypeStringParam extends GoRouteData final StringExtensionType optionalDefaultValue; } -class ExtenstionTypeStringDefaultParam extends GoRouteData - with _$ExtenstionTypeStringDefaultParam { - ExtenstionTypeStringDefaultParam({ +class ExtensionTypeStringDefaultParam extends GoRouteData + with _$ExtensionTypeStringDefaultParam { + ExtensionTypeStringDefaultParam({ this.s = const StringExtensionType('default'), }); final StringExtensionType s; } -class ExtenstionTypeIntParam extends GoRouteData with _$ExtenstionTypeIntParam { - ExtenstionTypeIntParam({ +class ExtensionTypeIntParam extends GoRouteData with _$ExtensionTypeIntParam { + ExtensionTypeIntParam({ required this.x, required this.requiredValue, this.optionalNullableValue, @@ -65,17 +69,17 @@ class ExtenstionTypeIntParam extends GoRouteData with _$ExtenstionTypeIntParam { final IntExtensionType optionalDefaultValue; } -class ExtenstionTypeIntDefaultParam extends GoRouteData - with _$ExtenstionTypeIntDefaultParam { - ExtenstionTypeIntDefaultParam({ +class ExtensionTypeIntDefaultParam extends GoRouteData + with _$ExtensionTypeIntDefaultParam { + ExtensionTypeIntDefaultParam({ this.x = const IntExtensionType(42), }); final IntExtensionType x; } -class ExtenstionTypeDoubleParam extends GoRouteData - with _$ExtenstionTypeDoubleParam { - ExtenstionTypeDoubleParam({ +class ExtensionTypeDoubleParam extends GoRouteData + with _$ExtensionTypeDoubleParam { + ExtensionTypeDoubleParam({ required this.d, required this.requiredValue, this.optionalNullableValue, @@ -87,8 +91,8 @@ class ExtenstionTypeDoubleParam extends GoRouteData final DoubleExtensionType optionalDefaultValue; } -class ExtenstionTypeNumParam extends GoRouteData with _$ExtenstionTypeNumParam { - ExtenstionTypeNumParam({ +class ExtensionTypeNumParam extends GoRouteData with _$ExtensionTypeNumParam { + ExtensionTypeNumParam({ required this.n, required this.requiredValue, this.optionalNullableValue, @@ -100,9 +104,8 @@ class ExtenstionTypeNumParam extends GoRouteData with _$ExtenstionTypeNumParam { final NumExtensionType optionalDefaultValue; } -class ExtenstionTypeBoolParam extends GoRouteData - with _$ExtenstionTypeBoolParam { - ExtenstionTypeBoolParam({ +class ExtensionTypeBoolParam extends GoRouteData with _$ExtensionTypeBoolParam { + ExtensionTypeBoolParam({ required this.b, required this.requiredValue, this.optionalNullableValue, @@ -114,9 +117,24 @@ class ExtenstionTypeBoolParam extends GoRouteData final BoolExtensionType optionalDefaultValue; } -class ExtenstionTypeBigIntParam extends GoRouteData - with _$ExtenstionTypeBigIntParam { - ExtenstionTypeBigIntParam({ +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, @@ -128,9 +146,9 @@ class ExtenstionTypeBigIntParam extends GoRouteData final BigIntExtensionType? optionalNullableValue; } -class ExtenstionTypeDateTimeParam extends GoRouteData - with _$ExtenstionTypeDateTimeParam { - ExtenstionTypeDateTimeParam({ +class ExtensionTypeDateTimeParam extends GoRouteData + with _$ExtensionTypeDateTimeParam { + ExtensionTypeDateTimeParam({ required this.dt, required this.optionalValue, this.optionalNullableValue, @@ -140,10 +158,23 @@ class ExtenstionTypeDateTimeParam extends GoRouteData 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 index aa8ad0054c8..d0e7638af9d 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart.expect @@ -1,49 +1,57 @@ -RouteBase get $extenstionTypeParam => GoRouteData.$route( +RouteBase get $extensionTypeParam => GoRouteData.$route( path: '/', - factory: _$ExtenstionTypeParam._fromState, + factory: _$ExtensionTypeParam._fromState, routes: [ GoRouteData.$route( path: 'string/:s', - factory: _$ExtenstionTypeStringParam._fromState, + factory: _$ExtensionTypeStringParam._fromState, ), GoRouteData.$route( path: 'string_default/:s', - factory: _$ExtenstionTypeStringDefaultParam._fromState, + factory: _$ExtensionTypeStringDefaultParam._fromState, ), GoRouteData.$route( path: 'int/:x', - factory: _$ExtenstionTypeIntParam._fromState, + factory: _$ExtensionTypeIntParam._fromState, ), GoRouteData.$route( path: 'int_default/:x', - factory: _$ExtenstionTypeIntDefaultParam._fromState, + factory: _$ExtensionTypeIntDefaultParam._fromState, ), GoRouteData.$route( path: 'double/:d', - factory: _$ExtenstionTypeDoubleParam._fromState, + factory: _$ExtensionTypeDoubleParam._fromState, ), GoRouteData.$route( path: 'num/:n', - factory: _$ExtenstionTypeNumParam._fromState, + factory: _$ExtensionTypeNumParam._fromState, ), GoRouteData.$route( path: 'bool/:b', - factory: _$ExtenstionTypeBoolParam._fromState, + factory: _$ExtensionTypeBoolParam._fromState, + ), + GoRouteData.$route( + path: 'enum/:value', + factory: _$ExtensionTypeEnumType._fromState, ), GoRouteData.$route( path: 'bigint/:bi', - factory: _$ExtenstionTypeBigIntParam._fromState, + factory: _$ExtensionTypeBigIntParam._fromState, ), GoRouteData.$route( path: 'datetime/:dt', - factory: _$ExtenstionTypeDateTimeParam._fromState, + factory: _$ExtensionTypeDateTimeParam._fromState, + ), + GoRouteData.$route( + path: 'uri/:uri', + factory: _$ExtensionTypeUriType._fromState, ), ], ); -mixin _$ExtenstionTypeParam on GoRouteData { - static ExtenstionTypeParam _fromState(GoRouterState state) => - ExtenstionTypeParam(); +mixin _$ExtensionTypeParam on GoRouteData { + static ExtensionTypeParam _fromState(GoRouterState state) => + ExtensionTypeParam(); @override String get location => GoRouteData.$location( @@ -64,9 +72,9 @@ mixin _$ExtenstionTypeParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeStringParam on GoRouteData { - static ExtenstionTypeStringParam _fromState(GoRouterState state) => - ExtenstionTypeStringParam( +mixin _$ExtensionTypeStringParam on GoRouteData { + static ExtensionTypeStringParam _fromState(GoRouterState state) => + ExtensionTypeStringParam( s: state.pathParameters['s']! as StringExtensionType, requiredValue: state.uri.queryParameters['required-value']! as StringExtensionType, @@ -78,18 +86,18 @@ mixin _$ExtenstionTypeStringParam on GoRouteData { const StringExtensionType('default'), ); - ExtenstionTypeStringParam get _self => this as ExtenstionTypeStringParam; + ExtensionTypeStringParam get _self => this as ExtensionTypeStringParam; @override String get location => GoRouteData.$location( - '/string/${Uri.encodeComponent(_self.s.toString())}', + '/string/${Uri.encodeComponent(_self.s as String)}', queryParams: { - 'required-value': _self.requiredValue.toString(), + 'required-value': _self.requiredValue as String, if (_self.optionalNullableValue != null) - 'optional-nullable-value': _self.optionalNullableValue!.toString(), + 'optional-nullable-value': _self.optionalNullableValue! as String, if (_self.optionalDefaultValue != const StringExtensionType('default')) - 'optional-default-value': _self.optionalDefaultValue.toString(), + 'optional-default-value': _self.optionalDefaultValue as String, }, ); @@ -107,19 +115,19 @@ mixin _$ExtenstionTypeStringParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeStringDefaultParam on GoRouteData { - static ExtenstionTypeStringDefaultParam _fromState(GoRouterState state) => - ExtenstionTypeStringDefaultParam( +mixin _$ExtensionTypeStringDefaultParam on GoRouteData { + static ExtensionTypeStringDefaultParam _fromState(GoRouterState state) => + ExtensionTypeStringDefaultParam( s: state.pathParameters['s'] as StringExtensionType? ?? const StringExtensionType('default'), ); - ExtenstionTypeStringDefaultParam get _self => - this as ExtenstionTypeStringDefaultParam; + ExtensionTypeStringDefaultParam get _self => + this as ExtensionTypeStringDefaultParam; @override String get location => GoRouteData.$location( - '/string_default/${Uri.encodeComponent(_self.s.toString())}', + '/string_default/${Uri.encodeComponent(_self.s as String)}', ); @override @@ -136,9 +144,9 @@ mixin _$ExtenstionTypeStringDefaultParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeIntParam on GoRouteData { - static ExtenstionTypeIntParam _fromState(GoRouterState state) => - ExtenstionTypeIntParam( +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, @@ -151,7 +159,7 @@ mixin _$ExtenstionTypeIntParam on GoRouteData { const IntExtensionType(42), ); - ExtenstionTypeIntParam get _self => this as ExtenstionTypeIntParam; + ExtensionTypeIntParam get _self => this as ExtensionTypeIntParam; @override String get location => GoRouteData.$location( @@ -179,15 +187,15 @@ mixin _$ExtenstionTypeIntParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeIntDefaultParam on GoRouteData { - static ExtenstionTypeIntDefaultParam _fromState(GoRouterState state) => - ExtenstionTypeIntDefaultParam( +mixin _$ExtensionTypeIntDefaultParam on GoRouteData { + static ExtensionTypeIntDefaultParam _fromState(GoRouterState state) => + ExtensionTypeIntDefaultParam( x: int.tryParse(state.pathParameters['x'] ?? '') as IntExtensionType? ?? const IntExtensionType(42), ); - ExtenstionTypeIntDefaultParam get _self => - this as ExtenstionTypeIntDefaultParam; + ExtensionTypeIntDefaultParam get _self => + this as ExtensionTypeIntDefaultParam; @override String get location => GoRouteData.$location( @@ -208,9 +216,9 @@ mixin _$ExtenstionTypeIntDefaultParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeDoubleParam on GoRouteData { - static ExtenstionTypeDoubleParam _fromState(GoRouterState state) => - ExtenstionTypeDoubleParam( +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']!) @@ -224,7 +232,7 @@ mixin _$ExtenstionTypeDoubleParam on GoRouteData { const DoubleExtensionType(3.14), ); - ExtenstionTypeDoubleParam get _self => this as ExtenstionTypeDoubleParam; + ExtensionTypeDoubleParam get _self => this as ExtensionTypeDoubleParam; @override String get location => GoRouteData.$location( @@ -252,9 +260,9 @@ mixin _$ExtenstionTypeDoubleParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeNumParam on GoRouteData { - static ExtenstionTypeNumParam _fromState(GoRouterState state) => - ExtenstionTypeNumParam( +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, @@ -267,7 +275,7 @@ mixin _$ExtenstionTypeNumParam on GoRouteData { const NumExtensionType(3.14), ); - ExtenstionTypeNumParam get _self => this as ExtenstionTypeNumParam; + ExtensionTypeNumParam get _self => this as ExtensionTypeNumParam; @override String get location => GoRouteData.$location( @@ -295,9 +303,9 @@ mixin _$ExtenstionTypeNumParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeBoolParam on GoRouteData { - static ExtenstionTypeBoolParam _fromState(GoRouterState state) => - ExtenstionTypeBoolParam( +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, @@ -310,7 +318,7 @@ mixin _$ExtenstionTypeBoolParam on GoRouteData { const BoolExtensionType(true), ); - ExtenstionTypeBoolParam get _self => this as ExtenstionTypeBoolParam; + ExtensionTypeBoolParam get _self => this as ExtensionTypeBoolParam; @override String get location => GoRouteData.$location( @@ -338,9 +346,62 @@ mixin _$ExtenstionTypeBoolParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeBigIntParam on GoRouteData { - static ExtenstionTypeBigIntParam _fromState(GoRouterState state) => - ExtenstionTypeBigIntParam( +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']!) @@ -353,7 +414,7 @@ mixin _$ExtenstionTypeBigIntParam on GoRouteData { as BigIntExtensionType?, ); - ExtenstionTypeBigIntParam get _self => this as ExtenstionTypeBigIntParam; + ExtensionTypeBigIntParam get _self => this as ExtensionTypeBigIntParam; @override String get location => GoRouteData.$location( @@ -381,9 +442,9 @@ mixin _$ExtenstionTypeBigIntParam on GoRouteData { void replace(BuildContext context) => context.replace(location); } -mixin _$ExtenstionTypeDateTimeParam on GoRouteData { - static ExtenstionTypeDateTimeParam _fromState(GoRouterState state) => - ExtenstionTypeDateTimeParam( +mixin _$ExtensionTypeDateTimeParam on GoRouteData { + static ExtensionTypeDateTimeParam _fromState(GoRouterState state) => + ExtensionTypeDateTimeParam( dt: DateTime.parse(state.pathParameters['dt']!) as DateTimeExtensionType, optionalValue: @@ -394,7 +455,7 @@ mixin _$ExtenstionTypeDateTimeParam on GoRouteData { as DateTimeExtensionType?, ); - ExtenstionTypeDateTimeParam get _self => this as ExtenstionTypeDateTimeParam; + ExtensionTypeDateTimeParam get _self => this as ExtensionTypeDateTimeParam; @override String get location => GoRouteData.$location( @@ -419,3 +480,45 @@ mixin _$ExtenstionTypeDateTimeParam on GoRouteData { @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 From 9b9afeb6c9c46f12a886b3117b6e01dbb99bc8d3 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Tue, 1 Jul 2025 18:50:59 +0900 Subject: [PATCH 07/14] chore: Fix version --- packages/go_router_builder/CHANGELOG.md | 7 +++++-- packages/go_router_builder/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index 19f3072cdb5..211c42e7e49 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,7 +1,10 @@ -## 3.0.2 +## Next - Restricts `build` to versions less than 2.5.0. -- Support `extension type`. + +## 3.1.0 + +- Adds support for`extension type`. ## 3.0.1 diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml index 032a8a74bad..3d61386b7b5 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.0.2 +version: 3.1.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 From 4a4d8927cb72d92486a1b08a7b9d9d93e3d668ef Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Tue, 1 Jul 2025 18:52:57 +0900 Subject: [PATCH 08/14] chore: fix --- packages/go_router_builder/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index 211c42e7e49..2b5e7f4d1b5 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,4 +1,4 @@ -## Next +## NEXT - Restricts `build` to versions less than 2.5.0. From bcc5d26220284f0baf7c2fc93a8ae10ef8501fd2 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 3 Jul 2025 20:38:11 +0900 Subject: [PATCH 09/14] chore: Add example --- .../example/lib/all_extension_typs.dart | 408 ++++++++++++++ .../example/lib/all_extension_typs.g.dart | 502 ++++++++++++++++++ 2 files changed, 910 insertions(+) create mode 100644 packages/go_router_builder/example/lib/all_extension_typs.dart create mode 100644 packages/go_router_builder/example/lib/all_extension_typs.g.dart diff --git a/packages/go_router_builder/example/lib/all_extension_typs.dart b/packages/go_router_builder/example/lib/all_extension_typs.dart new file mode 100644 index 00000000000..cb6af6ac53c --- /dev/null +++ b/packages/go_router_builder/example/lib/all_extension_typs.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_typs.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_typs.g.dart b/packages/go_router_builder/example/lib/all_extension_typs.g.dart new file mode 100644 index 00000000000..e9b7723bb1c --- /dev/null +++ b/packages/go_router_builder/example/lib/all_extension_typs.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_typs.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; +} From c57f3d00630532f6cbcc3e725722aa5176097452 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Tue, 8 Jul 2025 07:43:06 +0900 Subject: [PATCH 10/14] fix: Fix typo --- .../lib/{all_extension_typs.dart => all_extension_types.dart} | 2 +- .../{all_extension_typs.g.dart => all_extension_types.g.dart} | 2 +- packages/go_router_builder/lib/src/type_helpers.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename packages/go_router_builder/example/lib/{all_extension_typs.dart => all_extension_types.dart} (99%) rename packages/go_router_builder/example/lib/{all_extension_typs.g.dart => all_extension_types.g.dart} (99%) diff --git a/packages/go_router_builder/example/lib/all_extension_typs.dart b/packages/go_router_builder/example/lib/all_extension_types.dart similarity index 99% rename from packages/go_router_builder/example/lib/all_extension_typs.dart rename to packages/go_router_builder/example/lib/all_extension_types.dart index cb6af6ac53c..a9949cd1eb5 100644 --- a/packages/go_router_builder/example/lib/all_extension_typs.dart +++ b/packages/go_router_builder/example/lib/all_extension_types.dart @@ -9,7 +9,7 @@ import 'package:go_router/go_router.dart'; import 'shared/data.dart'; -part 'all_extension_typs.g.dart'; +part 'all_extension_types.g.dart'; @TypedGoRoute(path: '/', routes: >[ TypedGoRoute( diff --git a/packages/go_router_builder/example/lib/all_extension_typs.g.dart b/packages/go_router_builder/example/lib/all_extension_types.g.dart similarity index 99% rename from packages/go_router_builder/example/lib/all_extension_typs.g.dart rename to packages/go_router_builder/example/lib/all_extension_types.g.dart index e9b7723bb1c..999e5bc607d 100644 --- a/packages/go_router_builder/example/lib/all_extension_typs.g.dart +++ b/packages/go_router_builder/example/lib/all_extension_types.g.dart @@ -2,7 +2,7 @@ // ignore_for_file: always_specify_types, public_member_api_docs -part of 'all_extension_typs.dart'; +part of 'all_extension_types.dart'; // ************************************************************************** // GoRouterGenerator diff --git a/packages/go_router_builder/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index 732fb612509..4418b37be2a 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -265,7 +265,7 @@ class _TypeHelperEnum extends _TypeHelperWithHelper { } /// A type helper for extension types. -/// Suppoted extension types are: +/// Supported extension types are: /// - [String] /// - [int] /// - [double] From 5910235944a23b8031275d4d193c52ccda5d9a5d Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 14 Aug 2025 07:24:14 +0900 Subject: [PATCH 11/14] feat: Element2 API --- packages/go_router_builder/lib/src/type_helpers.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router_builder/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index b8307b4763d..42785a3ec81 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -298,7 +298,7 @@ class _TypeHelperExtensionType extends _TypeHelper { @override String _decode( - ParameterElement parameterElement, Set pathParameters) { + FormalParameterElement parameterElement, Set pathParameters) { final DartType paramType = parameterElement.type; if (paramType.isNullableType && parameterElement.hasDefaultValue) { throw NullableDefaultValueError(parameterElement); From ffdb723e0a96cf8b94565734dbecf4b5a24ca3b5 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 14 Aug 2025 07:26:06 +0900 Subject: [PATCH 12/14] chore: 3.2.0 --- packages/go_router_builder/CHANGELOG.md | 8 ++++---- packages/go_router_builder/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index 419c27b3f8d..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`. @@ -6,10 +10,6 @@ - Improves test code formatting consistency. - Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. -## 3.1.0 - -- Adds support for`extension type`. - ## 3.0.1 - Updates README.md to use the mixin `with _$RouteName`. 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 From 9fc1ec20eba48dc825c2ac8c9318ece29f21ce65 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 14 Aug 2025 07:32:39 +0900 Subject: [PATCH 13/14] refactor: dart format --- .../lib/src/type_helpers.dart | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/go_router_builder/lib/src/type_helpers.dart b/packages/go_router_builder/lib/src/type_helpers.dart index 42785a3ec81..6741e21ad4f 100644 --- a/packages/go_router_builder/lib/src/type_helpers.dart +++ b/packages/go_router_builder/lib/src/type_helpers.dart @@ -298,7 +298,9 @@ class _TypeHelperExtensionType extends _TypeHelper { @override String _decode( - FormalParameterElement parameterElement, Set pathParameters) { + FormalParameterElement parameterElement, + Set pathParameters, + ) { final DartType paramType = parameterElement.type; if (paramType.isNullableType && parameterElement.hasDefaultValue) { throw NullableDefaultValueError(parameterElement); @@ -323,8 +325,9 @@ class _TypeHelperExtensionType extends _TypeHelper { '.$enumExtensionHelperName($stateValue) as $castType'; } - final String representationTypeName = - withoutNullability(representationType.getDisplayString()); + final String representationTypeName = withoutNullability( + representationType.getDisplayString(), + ); if (paramType.isNullableType || parameterElement.hasDefaultValue) { return "$representationTypeName.tryParse($stateValue ?? '') as $castType"; } else { @@ -361,12 +364,15 @@ class _TypeHelperExtensionType extends _TypeHelper { representationType.isDartCoreNum || representationType.isDartCoreBool || representationType.isEnum || - const TypeChecker.fromRuntime(BigInt) - .isAssignableFromType(representationType) || - const TypeChecker.fromRuntime(DateTime) - .isAssignableFromType(representationType) || - const TypeChecker.fromRuntime(Uri) - .isAssignableFromType(representationType); + const TypeChecker.fromRuntime( + BigInt, + ).isAssignableFromType(representationType) || + const TypeChecker.fromRuntime( + DateTime, + ).isAssignableFromType(representationType) || + const TypeChecker.fromRuntime( + Uri, + ).isAssignableFromType(representationType); } } From b8758e03801f448f837e4b54f6c52c1062502e93 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 14 Aug 2025 07:33:48 +0900 Subject: [PATCH 14/14] refactor: dart format (test_input) --- .../test_inputs/extension_type_parameter.dart | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/go_router_builder/test_inputs/extension_type_parameter.dart b/packages/go_router_builder/test_inputs/extension_type_parameter.dart index 04b6b4ae37e..976bb090fab 100644 --- a/packages/go_router_builder/test_inputs/extension_type_parameter.dart +++ b/packages/go_router_builder/test_inputs/extension_type_parameter.dart @@ -17,19 +17,22 @@ 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'), -]) +@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(); } @@ -71,9 +74,7 @@ class ExtensionTypeIntParam extends GoRouteData with _$ExtensionTypeIntParam { class ExtensionTypeIntDefaultParam extends GoRouteData with _$ExtensionTypeIntDefaultParam { - ExtensionTypeIntDefaultParam({ - this.x = const IntExtensionType(42), - }); + ExtensionTypeIntDefaultParam({this.x = const IntExtensionType(42)}); final IntExtensionType x; }