rrousselgit / freezed Goto Github PK
View Code? Open in Web Editor NEWCode generation for immutable classes that has a simple syntax/API without compromising on the features.
Home Page: https://pub.dev/packages/freezed
Code generation for immutable classes that has a simple syntax/API without compromising on the features.
Home Page: https://pub.dev/packages/freezed
In Dartz package we have Either<L,R> type to represent either L type or R type.
I thought that this should mimic this behavior
@immutable
abstract class Either<L, R> with _$Either<L, R> {
const factory Either.left(L left) = Left;
const factory Either.right(R right) = Right;
}
and usage
void run(Either<int, String> either) {
either.when(left: (value) => print(value), right: (value) => value);
}
---
run(Left(10));
However, I am getting the following error
lib/either.dart:7:39: Error: The constructor function type 'Left<dynamic, dynamic> Function(dynamic)' isn't a subtype of 'Either<L, R> Function(L)'.
- 'Left' is from 'package:sandbox/either.dart' ('lib/either.dart').
- 'Either' is from 'package:sandbox/either.dart' ('lib/either.dart').
const factory Either.left(L left) = Left;
^
lib/either.dart:8:41: Error: The constructor function type 'Right<dynamic, dynamic> Function(dynamic)' isn't a subtype of 'Either<L, R> Function(R)'.
- 'Right' is from 'package:sandbox/either.dart' ('lib/either.dart').
- 'Either' is from 'package:sandbox/either.dart' ('lib/either.dart').
const factory Either.right(R right) = Right;
^
Am I doing something wrong? Or should it be possible to create this? Thanks for any help.
Dart supports the part of
statement which unfortunately doesn't work with this otherwise amazing package.
I'm using the BLoC library and specify events and states as unions. I like to generate just one file instead of two, so I want to use the part of
statement in the event and state files which will point to the "core" bloc file.
part of 'sign_in_form_bloc.dart';
@immutable
abstract class SignInFormEvent with _$SignInFormEvent {
const factory SignInFormEvent.emailChanged(String emailStr) = _EmailChanged;
const factory SignInFormEvent.passwordChanged(String passwordStr) =
_PasswordChanged;
}
Then, the BLoC file contains the actual part '*.freezed.dart'
directive.
import 'package:flutter/foundation.dart';
part 'sign_in_form_event.dart';
part 'sign_in_form_state.dart';
part 'sign_in_form_bloc.freezed.dart';
class SignInFormBloc extends Bloc<SignInFormEvent, SignInFormState> {...}
While doing the things described above worked flawlessly with sum_types, freezed outputs a [SEVERE] error upon generation looking like this:
[SEVERE] freezed:freezed on lib/application/auth/sign_in_form/sign_in_form_bloc.dart:
Error formatting generated source code for package:workout_app_prep/application/auth/sign_in_form/sign_in_form_bloc.dartwhich was output to lib/application/auth/sign_in_form/sign_in_form_bloc.freezed.dart.
This may indicate an issue in the generated code or in the formatter.
Please check the generated code and file an issue on source_gen if appropriate.
Could not format because the source could not be parsed:
line 367, column 9: Unexpected text ')'.
╷
367 │ class _$) with DiagnosticableTreeMixin implements ) {
│ ^
╵
line 367, column 51: Expected a type name.
Basically it adds a DiagnosticableTreeMixin
to the generated file's classes.
Using only the part
statement as seen in the examples does work:
import 'package:flutter/foundation.dart';
part 'sign_in_form_event.freezed.dart';
@immutable
abstract class SignInFormEvent with _$SignInFormEvent {
const factory SignInFormEvent.emailChanged(String emailStr) = _EmailChanged;
const factory SignInFormEvent.passwordChanged(String passwordStr) =
_PasswordChanged;
}
Hi Remi,
maybe i am doing something wrong or i am not allowed to add 2 generics, but this class will
result in the error below
version: "0.1.3+1"
@immutable
abstract class Complete<Params, Value> with _$Complete {
const factory Complete.success(Params params, Value value) = Success;
const factory Complete.error(Params params, Object error) = Error;
}
will result in *
/// *.freezed.dart
'Error.copyWith' ('Error<Params, Value> Function({error: Object, params: Params})') isn't a valid override of '_$Complete.copyWith' ('Complete<dynamic, dynamic> Function({params: dynamic})').dart(invalid_override)
'Success.copyWith' ('Success<Params, Value> Function({params: Params, value: Value})') isn't a valid override of '_$Complete.copyWith' ('Complete<dynamic, dynamic> Function({params: dynamic})').dart(invalid_override)
It's relatively common to have to do a one-to-one mapping between the parameters of a callback and a constructor, like:
onError: (err) => MyClass.error(err),
Freezed could, in theory, generate placeholder classes with static methods to reduce the boilerplate of such thing:
onError: $MyClass.error,
It could be nice to use Unions for the BLOC states
But states usually have inheritance:
abstract class AppState {}
abstract class AppNavigationState {}
class AppNavigateToAState extends AppState with AppNavigationState {}
class AppNavigateToBState extends AppState with AppNavigationState {}
class AppSomeOtherState extends AppState {}
so we could filter states after:
.appBloc.whereType<AppNavigationState>()
so it could befreezed freezedWith attribute for example:
abstract class AppNavigationState {}
@freezed
abstract class AppState with _$Union {
@freezedWith(AppNavigationState)
const factory AppState.navigateToA() = NavigateToA;
@freezedWith(AppNavigationState)
const factory AppState.navigateToB() = NavigateToB;
}
File:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'second_state.freezed.dart';
part 'second_state.g.dart';
@freezed
abstract class SecondState with _$SecondState {
const factory SecondState({
@JsonKey(ignore: true, nullable: true) String dateTime,
@JsonKey(ignore: true, nullable: true) String uuid,
}) = _SecondState;
const factory SecondState.fromJson(Map<String, dynamic> json) => _$SecondStateFromJson(json);
}
Error:
[SEVERE] freezed:freezed on lib/state/second_state/second_state.dart:
Error running FreezedGenerator
Marked SecondState with @freezed, but freezed has nothing to generate
package:example/state/second_state/second_state.dart:6:16
╷
6 │ abstract class SecondState with _$SecondState {
│ ^^^^^^^^^^^
╵
Did I forget something? All other state files fail as well (so it's not the fact that both fields have ignore: true at JsonKey)
What's the reason for Result when<Result extends Object>
? With the extends Object
restriction it isn't possible to return Future<T>
or FutureOr<T>
How do you imagine to use when with Futures
or FutureOr
?
With Result when<Result>
it simply works but I don't know if other code requires a Object
somewhere.
Hi there,
first of all: Great package!
The only thing I am missing at this point is the possibility to ensure fields to be non null.
Normally I would use assert(field != null)
in the constructor to make a field non-nullable.
It would be really useful to have this feature on freezed classes to make nullability errors easier to prevent.
Thanks!
As title says, wouldn't this make sense?
it would only generate the JsonKey if there is a .fromJson method and no JsonKey annotation is used for the property.
abstract class Example with _$Example {
factory Example(@JsonKey(name: 'whatever') String value) = _Example;
}
Hi,
I am using freezed for serialisation. But getting the following error Try correcting the name to the name of an existing method, or defining a method named 'fromJson'
, I also defined factory constructor in the model class.
factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json);
Here is reproducible example
Flutter 1.12.13+hotfix.7 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 9f5ff2306b (3 weeks ago) • 2020-01-26 22:38:26 -0800
Engine • revision a67792536c
Tools • Dart 2.7.0
Operating System: Windows10(64bit)
Edited to include a better description of the issue (@rrousselGit)
Consider:
@immutable
abstract class Foo with _$Foo {
factory Foo(RedirectedName value) = RedirectedName;
}
This is a recursive class, as the default constructor somehow refers to itself.
But since RedirectedName
has yet to be generated, Freezed
does not understand what is happening and parse RedirectedName
as dynamic
instead.
Original message:
Hello, Thanks for your great package.
I created a simple class with 2 constructor with the following code but when I want to use price
property in PriceWithQuantity
class it just return dynamic instated of Price
class
@immutable
abstract class PriceExample with _$PriceExample {
const factory PriceExample.price(
int id,
int price,
) = Price;
const factory PriceExample.withQuantity(
Price price,
int quantity,
) = PriceWithQuantity;
}
This will not compile, or at least, no _$Person will be created.
Is this something you don't want/or is impossible to support?
@immutable
abstract class Person with _$Person {
const factory Person(String firstName, String lastName) = _Person;
String get fullName => '$firstName $lastName';
}
I'd love to be able to define custom toString
methods for data classes.
@immutable
abstract class ClubId with _$ClubId {
const factory ClubId(String value) = _ClubIdDataClass;
@override
String toString() {
return 'ClubId#$value';
}
}
It should skip generation of toString
in _$_ClubIdDataClass
and rely on the implementation in ClubId
.
Should be only possible for @immutable
annotated classes with one factory constructor. Alternatively with a custom @freezedDataClass
annotation which only allows one factory constructor
Thank you for the 0.7.0 update, the default values made my code a little bit simpler :)
One thing that I still have to work around are non-constant default values. My type has a String
property called id
that - if left empty - should have a random UUID value by default. I now work around this by adding a second factory method that redirects to the generated one without the id
parameter.
It would be nice if the @Default
annotation could also take in a lambda to generate the default value.
First of all great package! 👍
Would it be possible to add a selective ==
method when generating code?
Example:
class Person{
final String name;
final int age;
final double height;
final int personId;
const Person(this.name, this.age, this.height, this.personId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age &&
height == other.height &&
personId == other.personId;
@override
int get hashCode =>
name.hashCode ^
age.hashCode ^
height.hashCode ^
personId.hashCode;
}
Above class ==
method will check equality of every field in the class
In some cases, I would like to just check specific fields
class Person{
final String name;
final int age;
final double height;
final int personId;
const Person(this.name, this.age, this.height, this.personId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
personId == other.personId;
@override
int get hashCode => personId.hashCode;
}
Above class ==
method will check equality of specified field personId;
Thanks!
(great job Remi!)
I wasn't sure how to title this! It isn't possible to do the following because the properties are only available if all factory methods have that property. How is it best to overcome this?
abstract class Result with _$Result {
const factory Result.Continue(Action action) = Result_Continue;
const factory Result.Retry(Action action) = Result_Retry;
const factory Result.Complete() = Result_Complete;
}
blah(Result result){
if(result is Result_Continue || result is Result_Retry){
process(result.action); //. <----- this is not possible because action is not on all factorys
}
I thought that composition might do it, is that the best way? Is it just a different way of thinking than my inheritance conditioned brain?
@freezed
abstract class ResultType with _$Result {
const factory ResultType.Continue(Action action) = ResultType_Continue;
const factory ResultType.Retry(Action action) = ResultType_RetryError;
}
@freezed
abstract class Result with _$Result {
const factory Result.ResultType(ResultType resultType) = Result_Type;
const factory Result.Complete(WadFile wadFile) = Result_Complete;
}
blah(Result result){
if(result is Result_Type){
process(result.resultType.action);
}
Hey!
I was wondering whether it would be better to introduce a custom annotation if one wants to add the generator to an existing project which uses @immutable
already for the analyzer.
What do you think?
Sometimes we have a union with common properties between the different possibilities:
@immutable
abstract class TodoList with _$TodoList {
factory TodoList.data(List<Todo> todos) = TodoListData;
factory TodoList.loading(List<Todo> todos) = TodoListLoading;
factory TodoList.error(List<Todo> todos, [String message]) = TodoListError;
}
In such a situation, it is common to want to convert from one state to another, while preserving the common properties.
Currently, we have to use do it ourselves:
var todo = TodoList.data([]);
todo = TodoList.loading(todo.todos);
todo = TodoList.error(toto.todos, 'Error');
But we could simplify that by having a copyWith
that converts to a new type:
var todo = TodoList.data([]);
todo = todo.copyAsLoading();
todo = todo.copyAsError(message: 'Error');
As mentioned in #56, it's useful to have custom methods inside a freezed class. I would argue that those methods should be able to be written manually, not just generated. Something like #11 but with methods.
I agree that copy-pasting the implementation is not the nicest solution. For now, though, I have to resort to extensions.
Hi.
First, I'm really having fun with this package, will be very useful.
Working with json_serializable, a couple of warnings appear in the g.dart, related to the linter.
Name non-constant identifiers using lowerCamelCase.
for FromJson and ToJson method.
This is my freezed class:
@freezed
abstract class Station with _$Station {
factory Station({String description, String image, String name, String url}) =
_Station;
factory Station.fromJson(Map<String, dynamic> json) =>
_$StationFromJson(json);
}
And the g.dart file have this:
_$_Station _$_$_StationFromJson(Map<String, dynamic> json) {
return _$_Station(
description: json['description'] as String,
image: json['image'] as String,
name: json['name'] as String,
url: json['url'] as String,
);
}
Map<String, dynamic> _$_$_StationToJson(_$_Station instance) =>
<String, dynamic>{
'description': instance.description,
'image': instance.image,
'name': instance.name,
'url': instance.url,
};
I think is adding an extra '_$'. 🤔
There are multiple situations when we want to decorate a property (such as json #13 #15) or @deprecated
, ...
As such, it'd be great if we could annotate the constructor parameter to annotate the property:
@immutable
abstract class Example with _$Example {
factory Example({@deprecated String value}) = _Example;
}
It would be very useful, especially if Union classes are declared privately, to have getter generated which checks if the class is of a certain type. Additionally a getter to cast an object to a nested union type would be useful.
Example:
@immutable
abstract class AuthState with _$AuthState {
const factory AuthState.authorized(User user) = _AuthStateAuthorized;
const factory AuthState.unauthorized() = _AuthStateUnauthorized;
}
// Some logic which needs user to be authorized
if (!authState.isAuthorized) return;
final authorizedState = authState.asAuthorized; // Returns authState as _AuthStateAuthorized
A potential solution for #49
The principle is, any decorator added on a @freezed
constructor would be transposed to the generated class.
This means that if we write:
@freezed
abstract class Example with _$Example {
@deprecated
factory Example() = GeneratedClass;
}
This would lead to:
@deprecated
class GeneratedClass {
...
}
Hi there,
great progress so far on the project, I like the new additions!
I get a warning in all generated freezed files which says:
Parameters can't override default values, this method overrides 'DiagnosticableMixin.toString' where 'minLevel' has a different value.
It refers to the minLevel
parameter of the toString
method in the _$_Model
class.
Nothing too serious for now, but it would be nice to get rid of this warning.
Thanks!
It seems that static getters on @freezed
classes are broken since 0.4.0.
The following file:
test.dart:
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'test.freezed.dart';
part 'test.g.dart';
@freezed
abstract class Test with _$Test{
const factory Test() = _Test;
static int get value => 0;
}
works fine on 0.3.0, but on 0.4.0+ I get this error:
[SEVERE] freezed:freezed on lib/test.dart:
Error running FreezedGenerator
@freezed cannot be used on classes with unimplemented getters
package:freezed_test/test.dart:9:16
╷
9 │ abstract class Test with _$Test{
I don't see a reason why static getters would not be supported, I think that's a bug.
static methods still work.
It could be nice to have toString overloads, specially for the lists and complex objects to limit BLOC log for example.
We could use freezedToString attribute for example:
@freezed
abstract class Union with _$Union {
const factory Union.loaded(@freezedToString((i) => i.length) List<Item> items) = Loaded;
const factory Union.loading() = Loading;
}
and for the data classes it could be freezedNoToString attribute for example:
@freezedNoToString
abstract class ClubId with _$ClubId {
const factory ClubId(String value) = _ClubIdDataClass;
@override
String toString() {
return 'ClubId#$value';
}
}
I have not even added any union code to my pure dart project and build_runner results in following error:
$ pub run build_runner watch
[SEVERE] Failed to snapshot build script .dart_tool/build/entrypoint/build.dart.
This is likely caused by a misconfigured builder definition.
[SEVERE] ../../../../.pub-cache/hosted/pub.dartlang.org/freezed-0.1.0/lib/src/generator.dart:97:99: Error: The method 'getDisplayString' isn't defined for the class 'DartType'. - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.0/lib/dart/element/type.dart').Try correcting the name to the name of an existing method, or defining a method named 'getDisplayString'. for (final property in commonProperties) Getter(name: property.name, type: property.type?.getDisplayString()), ^^^^^^^^^^^^^^^^../../../../.pub-cache/hosted/pub.dartlang.org/freezed-0.1.0/lib/src/generator.dart:121:64: Error: The method 'getDisplayString' isn't defined for the class 'DartType'. - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.0/lib/dart/element/type.dart').Try correcting the name to the name of an existing method, or defining a method named 'getDisplayString'. Property(name: property.name, type: property.type?.getDisplayString()) ^^^^^^^^^^^^^^^^../../../../.pub-cache/hosted/pub.dartlang.org/freezed-0.1.0/lib/src/generator.dart:124:55: Error: The method 'getDisplayString' isn't defined for the class 'DartType'. - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.0/lib/dart/element/type.dart').Try correcting the name to the name of an existing method, or defining a method named 'getDisplayString'. return Property(name: p.name, type: p.type?.getDisplayString()); ^^^^^^^^^^^^^^^^../../../../.pub-cache/hosted/pub.dartlang.org/freezed-0.1.0/lib/src/templates/parameter_template.dart:43:25: Error: The method 'getDisplayString' isn't defined for the class 'DartType'. - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.0/lib/dart/element/type.dart').Try correcting the name to the name of an existing method, or defining a method named 'getDisplayString'. type: e.type?.getDisplayString(), ^^^^^^^^^^^^^^^^../../../../.pub-cache/hosted/pub.dartlang.org/freezed-0.1.0/lib/src/templates/parameter_template.dart:51:23: Error: The method 'getDisplayString' isn't defined for the class 'DartType'. - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.0/lib/dart/element/type.dart').Try correcting the name to the name of an existing method, or defining a method named 'getDisplayString'. type: e.type?.getDisplayString(), ^^^^^^^^^^^^^^^^
Same happens when I add freezed union code.
Pub 2.7.0
analyzer 0.39.0
build_runner 1.7.4
freezed 0.1.0
source_gen 0.9.4+7
after creating these model classes on version 0.2.4...
/// model.dart
import 'package:json_annotation/json_annotation.dart';
import 'package:flutter/foundation.dart';
part 'model.freezed.dart';
part 'model.g.dart';
@immutable
abstract class Parent<T> with _$Parent<T> {
const factory Parent(@DataConverter() List<T> items) = _Parent<T>;
factory Parent.fromJson(Map<String, dynamic> json) =>
_$ParentFromJson<T>(json);
}
@immutable
abstract class Item with _$Item {
const factory Item(String name) = _Item;
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
}
class DataConverter<T> implements JsonConverter<T, Object> {
const DataConverter();
@override
T fromJson(Object json) {
return Item.fromJson(json) as T;
}
@override
Object toJson(T object) {
return object;
}
}
... i am going to run the default sample project. ( flutter create ). It will result in the following error:
lib/model.freezed.dart:74:57: Error: The constructor function type '_$_Parent<dynamic> Function(Map<String, dynamic>)' isn't a subtype of '_Parent<T> Function(Map<String, dynamic>)'.
- '_$_Parent' is from 'package:freezed_test/model.dart' ('lib/model.dart').
- 'Map' is from 'dart:core'.
- '_Parent' is from 'package:freezed_test/model.dart' ('lib/model.dart').
factory _Parent.fromJson(Map<String, dynamic> json) = _$_Parent.fromJson;
I can fix the error by opening model.freezed and change
/// model.freezed.dart:74
factory _Parent.fromJson(Map<String, dynamic> json) = _$_Parent.fromJson;
to
factory _Parent.fromJson(Map<String, dynamic> json) = _$_Parent<T>.fromJson;
for collections need to implement DeepCollectionEquality.
To make it easier for new consumer it would be great to add better error messages if things are misconfigured. So far error messages look rather cryptic.
For example I have the following code:
@freezed
@immutable
abstract class QAccount1 with _$QAccount1 {
factory QAccount1({
@JsonKey(name: 'email') @required String id,
@JsonKey(name: 'username') String name,
@JsonKey(name: 'avatar_url') String avatarUrl,
@JsonKey(name: 'extras', nullable: true) Map<String, dynamic> extras,
@JsonKey(name: 'last_comment_id') int lastMessageId,
@JsonKey(name: 'last_sync_event_id') int lastEventId,
}) = _QAccount1;
factory QAccount1.fromJson(Map<String, dynamic> json) =>
_$QAccount1FromJson(json);
}
Freezed should generate:
// This is taken from json_serializable
QAccount1 _$QAccountFromJson(Map<String, dynamic> json) {
return QAccount1(
id: json['email'] as String,
name: json['username'] as String,
avatarUrl: json['avatar_url'] as String,
extras: json['extras'] as Map<String, dynamic>,
lastMessageId: json['last_comment_id'] as int,
lastEventId: json['last_sync_event_id'] as int,
);
}
but the actual generated code are:
_$_QAccount1 _$_$_QAccount1FromJson(Map<String, dynamic> json) {
return _$_QAccount1(
id: json['id'] as String,
name: json['name'] as String,
avatarUrl: json['avatarUrl'] as String,
extras: json['extras'] as Map<String, dynamic>,
lastMessageId: json['lastMessageId'] as String,
lastEventId: json['lastEventId'] as String,
);
}
As you can see, generated code still using id
instead of email
Generated classes containing Lists violate the contract of the hashCode
function:
Hash codes must be the same for objects that are equal to each other according to [operator ==].
For the simple class:
@freezed
abstract class Data with _$Data {
factory Data({
List<String> keys,
}) = _Data;
}
the following test fails due to different hashCode
values
void main() {
test("test", () {
Data buildItem() {
return Data(keys: ["VALUE"]);
}
final data1 = buildItem();
final data2 = buildItem();
expect(data1, equals(data2));
expect(data1.hashCode, equals(data2.hashCode));
});
}
this feature is already available in json_serializable package:
@JsonSerializable()
class Response<T>{
int status;
T data;
Response(this.status,this.data);
factory Response.fromJson(Map<String, dynamic> json) =>
_$ResponseFromJson(json);
Map<String, dynamic> toJson() => _$ResponseToJson(this);
}
@JsonSerializable()
class PersonResponse {
Person person;
PersonResponse(this.person);
factory PersonResponse.fromJson(Map<String, dynamic> json) =>
_$PersonResponseFromJson(json);
Map<String, dynamic> toJson() => _$PersonResponseToJson(this);
}
@JsonSerializable()
class PeopleResponse {
final int count;
final List<Person> people;
PeopleResponse(this.count, this.people);
factory PeopleResponse.fromJson(Map<String, dynamic> json) =>
_$PeopleResponseFromJson(json);
Map<String, dynamic> toJson() => _$PeopleResponseToJson(this);
}
@JsonSerializable()
class Person {
String name ;
int age;
Person(this.name,this.age);
factory Person.fromJson(Map<String, dynamic> json) =>
_$PersonFromJson(json);
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
where T could be PersonResponse or PeopleResponse
you can't run this code because the generator will raise this error
Could not generate `fromJson` code for `data`.
None of the provided `TypeHelper` instances support the defined type.
this code won't work until i provide custom converter to the generic variable:
class DataConverter<T> implements JsonConverter<T, Object>{
const DataConverter();
@override
T fromJson(Object json) {
final data = json as Map<String, dynamic>;
if (data.containsKey('person')) {
return PersonResponse.fromJson(data) as T;
}
return PeopleResponse.fromJson(data) as T;
}
@override
Object toJson(T object) {
if (object is PersonResponse) {
return object.toJson();
}
return (object as PeoplePayload).toJson();
}
}
then modify the Response class to work as expected:
@JsonSerializable()
class Response<T> {
int status;
@DataConverter() // <--- custom converter annotation
T data;
Response(this.status, this.data);
factory Response.fromJson(Map<String, dynamic> json) =>
_$ResponseFromJson(json);
Map<String, dynamic> toJson() => _$ResponseToJson(this);
}
i suggest you add this annotaion to the factory constructor:
@immutable
class Response<T>{
factory Response(
int status,
@DataConverter() T data, // <--- custom converter annotation
) = _Response;
factory Response.fromJson(Map<String, dynamic> json) => _$ResponseFromJson(json);
}
Hi there! Your package looks very promising - we can't wait to replace built_value with it.
This is what I found out while trying the laaaatest version:
// main.dart
import 'package:flutter/foundation.dart';
import 'package:json_annotation/json_annotation.dart';
part 'model.freezed.dart';
part 'model.g.dart';
@immutable
abstract class Model with _$Model {
factory Model({String a, List<Model> models}) = _Model;
factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json);
}
main() {
var model = Model(
a: 'hol',
models: [
Model(
a: 'no',
models: const [],
),
],
);
model.copyWith(a: 't'); // => no
}
In the last line copyWith is not accessible - I could, however, use _Model instead
Consider:
@immutable
abstract class Example with _$Example {
const factory Example(String a, {int b}) = Example0;
factory Example.fixed() {
return const Example('a', b: 42);
}
}
This shouldn't generate code for fixed
Do you have an example of how I can implement inheritance? E.g. type animal has a name and type cat has a name and a color...
consider this sample:
@freezed
abstract class DefaultList
with _$DefaultList {
const factory DefaultList(
{@Default(<String>[]) List<String> values,}) = _DefaultList;
}
this will generate this code:
class _$_DefaultList with DiagnosticableTreeMixin implements _DefaultList {
const _$_DefaultList({@Default(<String>[]) this.values = <String>[] // <- this should be const!
});
...
which won't compile as the default value of values
is not constant in the generated code.
Since parameters of annotations are implicitly constant, users can omit the "const" modifier there, and will get a warning if they use it in an already constant context if they use pedantic.
freezed should insert the "const" modifier if the user omits it in the @Default
annotation for list literals, set literals, map literals and classes with const constructors.
ex:
@JsonSerializable(fieldRename: FieldRename.snake)
class Person {
// will convert camal case to snake case automatically
// equivelant to JsonKey(name:"first_name")
String firstName;
}
suggestion:
@SerializableFreezed(fieldRename: FieldRename.snake)
class Person {
factory Person(String firstName) = _Person;
}
… but I hope that I will not be notified everyday as today 😉
While factory constructors using factory Class() = Something;
can't define default values, we could have:
abstract class Example with _$Example {
factory Example(@Default(42) int value) = _Example;
}
This would also be added to @JsonKey
if none are specified:
@JsonKey(defaultValue: 42)
Hey,
I currently get a build error due to an error that the argument type can't be assigned to the parameter type:
@immutable
abstract class Example with _$Example {
factory Example.something() = Something;
factory Example.error(Error error) = SomeError;
}
This happens in the generated when
and maybeWhen
in _$SomeError
when the passed error
function will be called with the field error
. All parameters should be references with e.g. this.error
instead.
If you use the code from the example with generics and try to change the default factory putting a list as a parameter, you will encounter an error during the code generation:
part 'union.freezed.dart';
@immutable
abstract class Union<T> with _$Union<T> {
const factory Union(List<T> value) = Data<T>;
const factory Union.apiError(WebException webException) = ApiErrorDetails<T>;
const factory Union.connectionError() = ConnectionErrorDetails<T>;
}
Error formatting generated source code for [...] which was output to [...].
This may indicate an issue in the generated code or in the formatter.
Please check the generated code and file an issue on source_gen if appropriate.
Could not format because the source could not be parsed:
line 24, column 1: Expected to find ')'.
╷
24 │ }
│ ^
╵
line 24, column 1: Expected to find ')'.
╷
24 │ }
│ ^
╵
line 45, column 1: Expected to find ')'.
╷
45 │ }
│ ^
╵
line 57, column 1: Expected to find ')'.
╷
57 │ }
│ ^
╵
line 91, column 1: Expected to find ')'.
╷
91 │ }
│ ^
╵
line 91, column 1: Expected to find ')'.
╷
91 │ }
│ ^
╵
line 91, column 1: Expected to find ')'.
╷
91 │ }
│ ^
╵
line 91, column 1: Expected to find ')'.
╷
91 │ }
│ ^
╵
line 104, column 1: Expected to find ')'.
╷
104 │ }
│ ^
╵
line 104, column 1: Expected to find ')'.
╷
104 │ }
│ ^
╵
(8 more errors...)
[INFO] Running build completed, took 749ms
[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 34ms
[SEVERE] Failed after 790ms
Generated _$Union is abstract class. With lint rule 'prefer_mixin' link enabled there is warning.
Is there a special reason to have it as an abstract class?
Of course, this issue does not have any negative impact, just reason
Right now Freezed only support fromJson
.
But as showcased by Brian Egan's sample app, sometimes we may want to convert to and from a different class.
For example, instead of:
@freezed
abstract class Todo with _$Todo {
factory Todo(String task, {bool complete, String note, String id}) = _Todo;
factory Todo fromEntity(TodoEntity entity) {
return Todo(
entity.task,
complete: entity.complete ?? false,
note: entity.note,
id: entity.id,
);
}
}
extension TodoToEntity on Todo {
TodoEntity toEntity() {
return TodoEntity(task, id, note, complete);
}
}
We could write:
@freezed
abstract class Todo with _$Todo {
factory Todo(String task, {bool complete, String note, String id}) = _Todo;
factory Todo.fromEntity(TodoEntity entity) => _$TodoFromEntity(entity);
}
I did this little class:
@freezed
abstract class NumberTriviaEvent2 with _$NumberTriviaEvent2 {
const factory NumberTriviaEvent2.getTriviaForConcreteNumber2(
String numberString) = GetTriviaForConcreteNumber2;
const factory NumberTriviaEvent2.getTriviaForRandomNumber2() =
GetTriviaForRandomNumber2;
}
In the generated file I have:
mixin _$NumberTriviaEvent2 {
@optionalTypeArgs
Result when<Result extends Object>({
@required Result getTriviaForConcreteNumber2(String numberString),
@required Result getTriviaForRandomNumber2(),
}); . . .
. . .
But flutter complains:
... @required Result getTriviaForConcreteNumber2(String numberString), ...
and suggests this way:
... Result Function(String numberString) getTriviaForConcreteNumber2, ...
Hi Remi,
great work, again :)
FromJson(json) will generate code that requires a property called runtimeType in my map.
Planets _$PlanetsFromJson(Map<String, dynamic> json) {
assert(json['runtimeType'] is String);
switch (json['runtimeType'] as String) {
case 'default':
return _Planets.fromJson(json);
default:
throw FallThroughError();
}
}
Am i supposed to add runtimeType to every Map ?
Imagine the following response:
{
"count": 1,
"results": [
{
"name": "Alderaan"
}
]
}
2 Maps, 2 Models.
Planets {
List<Planet> planets
}
Planet {
String name
}
On my production project freezed is not generating g.dart file, the problem is that I could not reproduce this bug on a test project.
Creating a test project everything works fine, copying the working fine class of test to production and the .g.dart is not generated.
I tried do add all pubspec dependencies of production project at test project, and test still working.
json_serializable classes is working well when not generated by freezed.
I dont know what to do to find this bug, any idea?
Hi,
There's a problem when using property List of generated items, toJson method works incorrect.
@immutable
abstract class VideoModel with _$VideoModel {
const factory VideoModel({@JsonKey(name: 'id') String id,
@JsonKey(name: 'iso_639_1') String iso639,
@JsonKey(name: 'iso_3166_1') String iso3166,
@JsonKey(name: 'key') String key,
@JsonKey(name: 'name') String name,
@JsonKey(name: 'site') String site,
@JsonKey(name: 'size') int size,
@JsonKey(name: 'type') String type}) = _VideoModel;
factory VideoModel.fromJson(Map<String, dynamic> json) =>
_$VideoModelFromJson(json);
}
@immutable
abstract class DataVideosModel with _$DataVideosModel {
const factory DataVideosModel(
{@JsonKey(name: 'results') List<VideoModel> results,
@JsonKey(name: 'id') int id}) = _DataVideosModel;
factory DataVideosModel.fromJson(Map<String, dynamic> json) =>
_$DataVideosModelFromJson(json);
}
Currently toJson method is generated like:
Map<String, dynamic> _$_$_DataVideosModelToJson(_$_DataVideosModel instance) =>
<String, dynamic>{
'results': instance.results,
'id': instance.id,
};
Could you make supporting List in toJson method like this: (in that cases when List isn't List of primivite types)
Map<String, dynamic> _$_$_DataVideosModelToJson(_$_DataVideosModel instance) =>
<String, dynamic>{
'results': instance.results.map((result) => result.toJson()),
'id': instance.id,
};
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.