freezed
v3.2.5Code generation for immutable classes that has a simple syntax/API without compromising on the features.
Package archive: https://pubdev.letsnova.ru/api/archives/freezed/3.2.5.tar.gz
dart pub add freezedReadme
English | 한국어 | 简体中文 | 日本語 | Tiếng Việt
Welcome to Freezed, yet another code generator for data classes, tagged unions, nested classes and cloning.
Migration to 3.0.0
To migrate from 2.0.0 to 3.0.0, see changelog and our migration guide.
Motivation
Dart is awesome, but defining a "model" can be tedious. You have to:
- Define a constructor + properties
- Override
toString,operator ==,hashCode - Implement a
copyWithmethod to clone the object - Handle (de)serialization
Implementing all of this can take hundreds of lines, which are error-prone and affect the readability of your model significantly.
Freezed tries to fix that by implementing most of this for you, allowing you to focus on the definition of your model.
| Before | After |
|---|---|
![]() |
![]() |
Index
- Migration to 3.0.0
- Motivation
- Index
- How to use
- Utilities
How to use
Install
To use Freezed, you will need your typical build_runner/code-generator setup.
First, install build_runner and Freezed by adding them to your pubspec.yaml file:
For a Flutter project:
flutter pub add \
dev:build_runner \
freezed_annotation \
dev:freezed
# if using freezed to generate fromJson/toJson, also add:
flutter pub add json_annotation dev:json_serializable
For a Dart project:
dart pub add \
dev:build_runner \
freezed_annotation \
dev:freezed
# if using freezed to generate fromJson/toJson, also add:
dart pub add json_annotation dev:json_serializable
This installs three packages:
- build_runner, the tool to run code-generators
- freezed, the code generator
- freezed_annotation, a package containing annotations for freezed.
Disabling invalid_annotation_target warning and warning in generates files
If you plan on using Freezed in combination with json_serializable, recent
versions of json_serializable and meta may require you to disable the
invalid_annotation_target warning.
To do that, you can add the following to the analysis_options.yaml file
at the root of your project:
analyzer:
errors:
invalid_annotation_target: ignore
Run the generator
To run the code generator, execute the following command:
dart run build_runner watch -d
Note that like most code-generators, Freezed will need you to both import the annotation (freezed_annotation)
and use the part keyword on the top of your files.
As such, a file that wants to use Freezed will start with:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'my_file.freezed.dart';
CONSIDER also importing package:flutter/foundation.dart.
The reason being, importing foundation.dart also imports classes to make an
object nicely readable in Flutter's devtool.
If you import foundation.dart, Freezed will automatically do it for you.
Creating a Model using Freezed
Freezed offers two ways of creating data-classes:
- Primary constructors ; where you define a constructor and Freezed generates the associated fields.
This is simulating the Primary Constructor using
factory. - Classic classes, where you write a normal Dart class and Freezed only handles
toString/==/copyWith
Primary constructors
Freezed implements Primary Constructors by relying on factory constructors.
The idea is, you define a factory and Freezed generates everything else:
import 'package:freezed_annotation/freezed_annotation.dart';
// required: associates our `main.dart` with the code generated by Freezed
part 'main.freezed.dart';
// optional: Since our Person class is serializable, we must add this line.
// But if Person was not serializable, we could skip it.
part 'main.g.dart';
@freezed
abstract class Person with _$Person {
const factory Person({
required String firstName,
required String lastName,
required int age,
}) = _Person;
factory Person.fromJson(Map<String, Object?> json) => _$PersonFromJson(json);
}
The following snippet defines a model named Person:
Personhas 3 properties:firstName,lastNameandage- Because we are using
@freezed, all of this class's properties are immutable. - Since we defined a
fromJson, this class is de/serializable. Freezed will add atoJsonmethod for us. - Freezed will also automatically generate:
- a
copyWithmethod, for cloning the object with different properties - a
toStringoverride listing all the properties of the object - an
operator ==andhashCodeoverride (sincePersonis immutable)
- a
From this example, we can notice a few things:
-
It is necessary to annotate our model with
@freezed(or@Freezed/@unfreezed, more about that later).
This annotation is what tells Freezed to generate code for that class. -
We must also apply a mixin with the name of our class, prefixed by
_$. This mixin is what defines the various properties/methods of our object. -
When defining a constructor in a Freezed class, we should use the
factorykeyword as showcased (constis optional).
The parameters of this constructor will be the list of all properties that this class contains.
Parameters don't have to be named and required. Feel free to use positional optional parameters if you want!
Adding getters and methods to our models
Sometimes, you may want to manually define methods/properties in our classes.
But you will quickly notice that if you try to use primary constructors:
@freezed
abstract class Person with _$Person {
const factory Person(String name, {int? age}) = _Person;
void method() {
print('hello world');
}
}
then it will fail with the error The non-abstract class _$_Person is missing implementations for these members:.
For that to work, we need to define a private empty constructor. That will enable the generated code to extend/subclass our class, instead of implementing it (which is the default, and only inherits type, and not properties or methods):
@freezed
abstract class Person with _$Person {
// Added constructor. Must not have any parameter
const Person._();
const factory Person(String name, {int? age}) = _Person;
void method() {
print('hello world');
}
}
Asserts
Dart does not allow adding assert(...) statements to a factory constructor.
As such, to add asserts to your Freezed classes, you will need the @Assert decorator:
@freezed
abstract class Person with _$Person {
@Assert('name.isNotEmpty', 'name cannot be empty')
const factory Person({required String name, int? age}) = _Person;
}
Alternatively, you can specify a MyClass._() constructor:
@freezed
abstract class Person with _$Person {
Person._({required this.name})
: assert(name.isNotEmpty, 'name cannot be empty');
factory Person({required String name, int? age}) = _Person;
@override
final String name;
}
Default values
Similarly to asserts, Dart does not allow "redirecting factory constructors" to specify default values.
As such, if you want to specify default values for your properties,
you will need the @Default annotation:
@freezed
abstract class Example with _$Example {
const factory Example([@Default(42) int value]) = _Example;
}
NOTE:
If you are using serialization/deserialization, this will automatically add
a @JsonKey(defaultValue: <something>) for you.
Non-constant default values
If using @Default is not enough, you have two options:
- Either stop using primary constructors. See Classic Classes
- Add a
MyClass._()constructor to initialize said value
The latter is particularly helpful when writing large models, as this doesn't require writing a lot of code just for one default values.
One example would be the following:
@freezed
sealed class Response<T> with _$Response<T> {
// We give "time" parameters a non-constant default
Response._({DateTime? time}) : time = time ?? DateTime.now();
// Constructors may enable passing parameters to ._();
factory Response.data(T value, {DateTime? time}) = ResponseData;
// If ._ parameters are named and optional, factory constructors are not required to specify it
factory Response.error(Object error) = ResponseError;
@override
final DateTime time;
}
In this example, the field time is defaulting to DateTime.now().
Extending classes
You may want to have your Freezed class extend another class.
Unfortunately, factory does not allow specifying super(...).
As such, one workaround is to specify the MyClass._() again, similarly
to how we used it for non-constant default values. Here's an example:
class Subclass {
const Subclass.name(this.value);
final int value;
}
@freezed
abstract class MyFreezedClass extends Subclass with _$MyFreezedClass {
// We can receive parameters in this constructor, which we can use with `super.field`
const MyFreezedClass._(super.value) : super.name();
const factory MyFreezedClass(int value, /* other fields */) = _MyFreezedClass;
}
This syntax gives full control over inheritance.
Of course, you can also opt-out of factory constructors and write normal classes.
See Classic Classes.
In general, this workaround makes more sense for Unions, where
we have more than one factory constructor.
Defining a mutable class instead of an immutable one
So far, we've seen how to define a model where all of its properties are final;
but you may want to define mutable properties in your model.
Freezed supports this, by replacing the @freezed annotation with @unfreezed:
@unfreezed
abstract class Person with _$Person {
factory Person({
required String firstName,
required String lastName,
required final int age,
}) = _Person;
factory Person.fromJson(Map<String, Object?> json) => _$PersonFromJson(json);
}
This defines a model mostly identical to our previous snippets, but with the following differences:
-
firstNameandlastNameare now mutable. As such, we can write:void main() { var person = Person(firstName: 'John', lastName: 'Smith', age: 42); person.firstName = 'Mona'; person.lastName = 'Lisa'; } -
ageis still immutable, because we explicitly marked the property asfinal. -
Personno-longer has a custom ==/hashCode implementation:void main() { var john = Person(firstName: 'John', lastName: 'Smith', age: 42); var john2 = Person(firstName: 'John', lastName: 'Smith', age: 42); print(john == john2); // false } -
Of course, since our
Personclass is mutable, it is no-longer possible to instantiate it usingconst.
Allowing the mutation of Lists/Maps/Sets
By default when using @freezed (but not @unfreezed), properties of type List/Map/Set
are transformed to be immutable.
This means that writing the following will cause a runtime exception:
@freezed
abstract class Example with _$Example {
factory Example(List<int> list) = _Example;
}
void main() {
var example = Example([]);
example.list.add(42); // throws because we are mutating a collection
}
That behavior can be disabled by writing:
@Freezed(makeCollectionsUnmodifiable: false)
abstract class Example with _$Example {
factory Example(List<int> list) = _Example;
}
void main() {
var example = Example([]);
example.list.add(42); // OK
}
Classic classes
Instead of primary constructors, you can write normal Dart classes.
In this scenario, write a typical constructor + fields combo as you normally would:
import 'package:freezed_annotation/freezed_annotation.dart';
// required: associates our `main.dart` with the code generated by Freezed
part 'main.freezed.dart';
// optional: Since our Person class is serializable, we must add this line.
// But if Person was not serializable, we could skip it.
part 'main.g.dart';
@freezed
@JsonSerializable()
class Person with _$Person {
const Person({
required this.firstName,
required this.lastName,
required this.age,
});
@override
final String firstName;
@override
final String lastName;
@override
final int age;
factory Person.fromJson(Map<String, Object?> json)
=> _$PersonFromJson(json);
Map<String, Object?> toJson() => _$PersonToJson(this);
}
In this scenario, Freezed will generate copyWith/toString/==/hashCode,
but won't do anything related to JSON encoding (hence why you need to manually add @JsonSerializable).
This syntax has the benefit of enabling advanced constructor logic, such as inheritance or non-constant default values.
How copyWith works
As explained before, when defining a model using Freezed, then the code-generator
will automatically generate a copyWith method for us.
This method is used to clone an object with different values.
For example if we define:
@freezed
abstract class Person with _$Person {
factory Person(String name, int? age) = _Person;
}
Then we could write:
void main() {
var person = Person('Remi', 24);
// `age` not passed, its value is preserved
print(person.copyWith(name: 'Dash')); // Person(name: Dash, age: 24)
// `age` is set to `null`
print(person.copyWith(age: null)); // Person(name: Remi, age: null)
}
Notice Freezed supports person.copyWith(age: null).
Going further: Deep copy
While copyWith is very powerful in itself, it becomes inconvenient on more complex objects.
Consider the following classes:
@freezed
abstract class Company with _$Company {
const factory Company({String? name, required Director director}) = _Company;
}
@freezed
abstract class Director with _$Director {
const factory Director({String? name, Assistant? assistant}) = _Director;
}
@freezed
abstract class Assistant with _$Assistant {
const factory Assistant({String? name, int? age}) = _Assistant;
}
Then, from a reference on Company, we may want to perform changes on Assistant.
For example, to change the name of an assistant, using copyWith we would have to write:
Company company;
Company newCompany = company.copyWith(
director: company.director.copyWith(
assistant: company.director.assistant.copyWith(
name: 'John Smith',
),
),
);
This works, but is relatively verbose with a lot of duplicates.
This is where we could use Freezed's "deep copy".
If a Freezed model contains properties that are also Freezed models, then the code-generator will offer an alternate syntax to the previous example:
Company company;
Company newCompany = company.copyWith.director.assistant(name: 'John Smith');
This snippet will achieve strictly the same result as the previous snippet (creating a new company with an updated assistant name), but no longer has duplicates.
Going deeper in this syntax, if instead, we wanted to change the director's name then we could write:
Company company;
Company newCompany = company.copyWith.director(name: 'John Doe');
Overall, based on the definitions of Company/Director/Assistant mentioned above,
all the following "copy" syntaxes will work:
Company company;
company = company.copyWith(name: 'Google', director: Director(...));
company = company.copyWith.director(name: 'Larry', assistant: Assistant(...));
Null consideration
Some objects may also be null. For example, using our Company class,
then Director's assistant may be null.
As such, writing:
Company company = Company(name: 'Google', director: Director(assistant: null));
Company newCompany = company.copyWith.director.assistant(name: 'John');
doesn't make sense.
We can't change the assistant's name if there is no assistant to begin with.
In that situation, company.copyWith.director.assistant will return null,
causing our code to fail to compile.
To fix it, we can use the ?.call operator and write:
Company? newCompany = company.copyWith.director.assistant?.call(name: 'John');
Decorators and comments
Freezed supports property and class level decorators/documentation by decorating/documenting their respective parameter and constructor definition.
Consider:
@freezed
abstract class Person with _$Person {
const factory Person({
String? name,
int? age,
Gender? gender,
}) = _Person;
}
If you want to document name, you can do:
@freezed
abstract class Person with _$Person {
const factory Person({
/// The name of the user.
///
/// Must not be null
String? name,
int? age,
Gender? gender,
}) = _Person;
}
If you want to mark the property gender as @deprecated, then you can do:
@freezed
abstract class Person with _$Person {
const factory Person({
String? name,
int? age,
@deprecated Gender? gender,
}) = _Person;
}
This will deprecate both:
- The constructor
Person(gender: Gender.something); // gender is deprecated - The generated class's constructor:
_Person(gender: Gender.something); // gender is deprecated - the property:
Person person; print(person.gender); // gender is deprecated - the
copyWithparameter:Person person; person.copyWith(gender: Gender.something); // gender is deprecated
Similarly, if you want to decorate the generated class you can decorate the defining factory constructor.
As such, to deprecate _Person, you could do:
@freezed
abstract class Person with _$Person {
@deprecated
const factory Person({
String? name,
int? age,
Gender? gender,
}) = _Person;
}
FromJson/ToJson
While Freezed will not generate your typical fromJson/toJson by itself, it knows
what json_serializable is.
Making a class compatible with json_serializable is very straightforward.
Consider this snippet:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'model.freezed.dart';
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
}
The changes necessary to make it compatible with json_serializable consists of two lines:
- a new
part:part 'model.g.dart'; - a new constructor on the targeted class:
factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json);
The end result is:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'model.freezed.dart';
part 'model.g.dart';
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json);
}
Don't forget to add json_serializable to your pubspec.yaml file:
dev_dependencies:
json_serializable:
That's it!
With these changes, Freezed will automatically ask json_serializable to generate all the necessary
fromJson/toJson.
Note:
Freezed will only generate a fromJson if the factory is using =>.
fromJSON - classes with multiple constructors
For classes with multiple constructors, Freezed will check the JSON response
for a string element called runtimeType and choose the constructor to use based
on its value. For example, given the following constructors:
@freezed
sealed class MyResponse with _$MyResponse {
const factory MyResponse(String a) = MyResponseData;
const factory MyResponse.special(String a, int b) = MyResponseSpecial;
const factory MyResponse.error(String message) = MyResponseError;
factory MyResponse.fromJson(Map<String, dynamic> json) => _$MyResponseFromJson(json);
}
Then Freezed will use each JSON object's runtimeType to choose the constructor as follows:
[
{
"runtimeType": "default",
"a": "This JSON object will use constructor MyResponse()"
},
{
"runtimeType": "special",
"a": "This JSON object will use constructor MyResponse.special()",
"b": 42
},
{
"runtimeType": "error",
"message": "This JSON object will use constructor MyResponse.error()"
}
]
You can customize key and value with something different
using @Freezed and @FreezedUnionValue decorators:
@Freezed(unionKey: 'type', unionValueCase: FreezedUnionCase.pascal)
sealed class MyResponse with _$MyResponse {
const factory MyResponse(String a) = MyResponseData;
@FreezedUnionValue('SpecialCase')
const factory MyResponse.special(String a, int b) = MyResponseSpecial;
const factory MyResponse.error(String message) = MyResponseError;
factory MyResponse.fromJson(Map<String, dynamic> json) =>
_$MyResponseFromJson(json);
}
which would update the previous json to:
[
{
"type": "Default",
"a": "This JSON object will use constructor MyResponse()"
},
{
"type": "SpecialCase",
"a": "This JSON object will use constructor MyResponse.special()",
"b": 42
},
{
"type": "Error",
"message": "This JSON object will use constructor MyResponse.error()"
}
]
If you want to customize key and value for all the classes, you can specify it inside your
build.yaml file, for example:
targets:
$default:
builders:
freezed:
options:
union_key: type
union_value_case: pascal
If you don't control the JSON response, then you can implement a custom converter. Your custom converter will need to implement its own logic for determining which constructor to use.
class MyResponseConverter implements JsonConverter<MyResponse, Map<String, dynamic>> {
const MyResponseConverter();
@override
MyResponse fromJson(Map<String, dynamic> json) {
// type data was already set (e.g. because we serialized it ourselves)
if (json['runtimeType'] != null) {
return MyResponse.fromJson(json);
}
// you need to find some condition to know which type it is. e.g. check the presence of some field in the json
if (isTypeData) {
return MyResponseData.fromJson(json);
} else if (isTypeSpecial) {
return MyResponseSpecial.fromJson(json);
} else if (isTypeError) {
return MyResponseError.fromJson(json);
} else {
throw Exception('Could not determine the constructor for mapping from JSON');
}
}
@override
Map<String, dynamic> toJson(MyResponse data) => data.toJson();
}
To then apply your custom converter pass the decorator to a constructor parameter.
@freezed
abstract class MyModel with _$MyModel {
const factory MyModel(@MyResponseConverter() MyResponse myResponse) = MyModelData;
factory MyModel.fromJson(Map<String, dynamic> json) => _$MyModelFromJson(json);
}
By doing this, json serializable will use MyResponseConverter.fromJson() and MyResponseConverter.toJson() to convert MyResponse.
You can also use a custom converter on a constructor parameter contained in a List.
@freezed
abstract class MyModel with _$MyModel {
const factory MyModel(@MyResponseConverter() List<MyResponse> myResponse) = MyModelData;
factory MyModel.fromJson(Map<String, dynamic> json) => _$MyModelFromJson(json);
}
Note:
In order to serialize nested lists of freezed objects, you are supposed to either
specify a @JsonSerializable(explicitToJson: true) or change explicit_to_json
inside your build.yaml file (see the documentation).
Deserializing generic classes
In order to de/serialize generic typed freezed objects, you can enable genericArgumentFactories.
All you need to do is to change the signature of the fromJson method and add genericArgumentFactories: true to the freezed configuration.
@Freezed(genericArgumentFactories: true)
sealed class ApiResponse<T> with _$ApiResponse<T> {
const factory ApiResponse.data(T data) = ApiResponseData;
const factory ApiResponse.error(String message) = ApiResponseError;
factory ApiResponse.fromJson(Map<String, dynamic> json, T Function(Object?) fromJsonT) => _$ApiResponseFromJson(json, fromJsonT);
}
Alternatively, you can enable genericArgumentFactories for the whole project by modifying your build.yaml file to include the following:
targets:
$default:
builders:
freezed:
options:
generic_argument_factories: true
What about @JsonKey annotation?
All decorators passed to a constructor parameter are "copy-pasted" to the generated
property too.
As such, you can write:
@freezed
abstract class Example with _$Example {
factory Example(@JsonKey(name: 'my_property') String myProperty) = _Example;
factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
}
What about @JsonSerializable annotation?
You can pass @JsonSerializable annotation by placing it over constructor e.g.:
@freezed
abstract class Example with _$Example {
@JsonSerializable(explicitToJson: true)
factory Example(@JsonKey(name: 'my_property') SomeOtherClass myProperty) = _Example;
factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
}
If you want to define some custom json_serializable flags for all the classes (e.g. explicit_to_json or any_map) you can do it via build.yaml file as described here.
See also the decorators section
Union types
Coming from other languages, you may be used to features like "union types," "sealed classes," and pattern matching.
These are powerful tools in combination with a type system, but it isn't particularly ergonomic to use them in Dart.
But fear not, Freezed supports them, generating a few utilities to help you!
Long story short, in any Freezed class, you can write multiple constructors:
@freezed
sealed class Union with _$Union {
const factory Union.data(int value) = Data;
const factory Union.loading() = Loading;
const factory Union.error([String? message]) = Error;
}
By doing this, our model now can be in different mutually exclusive states.
In particular, this snippet defines a model Union, and that model has 3 possible states:
- data
- loading
- error
Note how we gave meaningful names to the right hand of the factory constructors we defined. They will come in handy later.
One thing you may also notice is that with this example, we can no longer write code such as:
void main() {
Union union = Union.data(42);
print(union.value); // compilation error: property value does not exist
}
We'll see why in the following section.
Shared properties
When defining multiple constructors, you will lose the ability to read properties that are not common to all constructors:
For example, if you write:
@freezed
sealed class Example with _$Example {
const factory Example.person(String name, int age) = Person;
const factory Example.city(String name, int population) = City;
}
Then you will be unable to read age and population directly:
var example = Example.person('Remi', 24);
print(example.age); // does not compile!
On the other hand, you can read properties that are defined on all constructors.
For example, the name variable is common to both Example.person and Example.city constructors.
As such we can write:
var example = Example.person('Remi', 24);
print(example.name); // Remi
example = Example.city('London', 8900000);
print(example.name); // London
The same logic can be applied to copyWith too.
We can use copyWith with properties defined on all constructors:
var example = Example.person('Remi', 24);
print(example.copyWith(name: 'Dash')); // Example.person(name: Dash, age: 24)
example = Example.city('London', 8900000);
print(example.copyWith(name: 'Paris')); // Example.city(name: Paris, population: 8900000)
On the other hand, properties that are unique to a specific constructor aren't available:
var example = Example.person('Remi', 24);
example.copyWith(age: 42); // compilation error, parameter `age` does not exist
To solve this problem, we need check the state of our object using what we call "pattern matching".
Using pattern matching to read non-shared properties
For this section, let's consider the following union:
@freezed
sealed class Example with _$Example {
const factory Example.person(String name, int age) = Person;
const factory Example.city(String name, int population) = City;
}
Let's see how we can use pattern matching to read the content of an Example instance.
For this, you should use Dart’s built-in pattern matching using switch:
switch (example) {
Person(:final name) => print('Person $name'),
City(:final population) => print('City ($population)'),
}
Alternatively, you could use an if-case statement:
if (example case Person(:final name)) {
print('Person $name');
} else if (example case City(:final population)) {
print('City ($population)');
}
You could also use is/as to cast an Example variable into either a Person or a City, but this is heavily discouraged. Use one of the other two options.
Mixins and Interfaces for individual classes for union types
When you have multiple types in the same class you might want one of those
types to implement an interface or mixin a class. You can do that using the
@Implements or @With decorators respectively. In the following example
City implements GeographicArea.
abstract class GeographicArea {
int get population;
String get name;
}
@freezed
sealed class Example with _$Example {
const factory Example.person(String name, int age) = Person;
@Implements<GeographicArea>()
const factory Example.city(String name, int population) = City;
}
This also works for implementing or mixing in generic classes e.g.
AdministrativeArea<House> except when the class has a generic type parameter
e.g. AdministrativeArea<T>. In this case freezed will generate correct code
but dart will throw a load error on the annotation declaration when compiling.
To avoid this you should use the @Implements.fromString and
@With.fromString decorators as follows:
abstract class GeographicArea {}
abstract class House {}
abstract class Shop {}
abstract class AdministrativeArea<T> {}
@freezed
sealed class Example<T> with _$Example<T> {
const factory Example.person(String name, int age) = Person<T>;
@With.fromString('AdministrativeArea<T>')
const factory Example.street(String name) = Street<T>;
@With<House>()
@Implements<Shop>()
@Implements<GeographicArea>()
@Implements.fromString('AdministrativeArea<T>')
const factory Example.city(String name, int population) = City<T>;
}
Note: You need to make sure that you comply with the interface requirements by implementing all the abstract members. If the interface has no members or just fields, you can fulfill the interface contract by adding them to the union type's constructor. Keep in mind that if the interface defines a method or a getter, that you implement in the class, you need to use the Adding getters and methods to our models instructions.
Note 2: You cannot use @With/@Implements with freezed classes.
Freezed classes can neither be extended nor implemented.
Ejecting an individual union case
To have fine-grained control over your models, Freezed offer the ability to manually write a subclass of a union.
Consider:
@freezed
sealed class Result<T> with _$Result {
factory Result.data(T data) = ResultData;
factory Result.error(Object error) = ResultError;
}
Now, let's say we wanted to write ResultData ourselves. For that, simply
define a ResultData class in the same file:
@freezed
sealed class Result<T> with _$Result {
factory Result.data(T data) = ResultData;
factory Result.error(Object error) = ResultError;
}
class ResultData<T> implements Result<T> {
// TODO: implement Result<T>
}
Note that the extracted class can be a Freezed class too!
@freezed
sealed class Result<T> with _$Result<T> {
const Result._();
const factory Result.data(T data) = ResultData;
const factory Result.error(Object error) = ResultError;
}
// TODO maybe add some methods unique to ResultData
@freezed
abstract class ResultData<T> extends Result<T> with _$ResultData<T> {
const factory ResultData(T data) = _ResultData;
const ResultData._() : super._();
}
(Legacy) Pattern matching utilities
[!WARNING] As of Dart 3, Dart now has built-in pattern-matching using sealed classes. As such, you no-longer need to rely on Freezed's generated methods for pattern matching. Instead of using
when/map, use the official Dart syntax.The references to
when/mapare kept for users who have yet to migrate to Dart 3. But in the long term, you should stop relying on them and migrate toswitchexpressions.
When
The [when] method is the equivalent to pattern matching with destructuring.
The prototype of the method depends on the constructors defined.
For example, with:
@freezed
sealed class Union with _$Union {
const factory Union(int value) = Data;
const factory Union.loading() = Loading;
const factory Union.error([String? message]) = ErrorDetails;
}
Then [when] will be:
var union = Union(42);
print(
union.when(
(int value) => 'Data $value',
loading: () => 'loading',
error: (String? message) => 'Error: $message',
),
); // Data 42
Whereas if we defined:
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
}
Then [when] will be:
var model = Model.first('42');
print(
model.when(
first: (String a) => 'first $a',
second: (int b, bool c) => 'second $b $c'
),
); // first 42
Notice how each callback matches with a constructor's name and prototype.
Map
The [map] methods are equivalent to [when], but without destructuring.
Consider this class:
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
}
With such class, while [when] will be:
var model = Model.first('42');
print(
model.when(
first: (String a) => 'first $a',
second: (int b, bool c) => 'second $b $c'
),
); // first 42
[map] will instead be:
var model = Model.first('42');
print(
model.map(
first: (First value) => 'first ${value.a}',
second: (Second value) => 'second ${value.b} ${value.c}'
),
); // first 42
This can be useful if you want to do complex operations, like [copyWith]/toString for example:
var model = Model.second(42, false)
print(
model.map(
first: (value) => value,
second: (value) => value.copyWith(c: true),
)
); // Model.second(b: 42, c: true)
Configurations
Freezed offers various options to customize the generated code. To do so, there are two possibilities:
Changing the behavior for a specific model
If you want to customize the generated code for only one specific class, you can do so by using a different annotation:
@Freezed()
abstract class Person with _$Person {
factory Person(String name, int age) = _Person;
}
By doing so, you can now pass various parameters to @Freezed to change the output:
@Freezed(
// Disable the generation of copyWith/==
copyWith: false,
equal: false,
)
abstract class Person with _$Person {...}
To view all the possibilities, see the documentation of @Freezed: https://pub.dev/documentation/freezed_annotation/latest/freezed_annotation/Freezed-class.html
Changing the behavior for the entire project
Instead of applying your modification to a single class, you may want to apply it to all Freezed models at the same time.
You can do so by customizing a file called build.yaml
This file is an optional configuration file that should be placed next to your pubspec.yaml:
my_project_folder/
pubspec.yaml
build.yaml
lib/
There, you will be able to change the same options as the options found in @Freezed (see above)
by writing:
targets:
$default:
builders:
freezed:
options:
# Tells Freezed to format .freezed.dart files.
# This can significantly slow down code-generation.
# Disabling formatting will only work when opting into Dart 3.7 as a minimum
# in your project SDK constraints.
format: true
# Disable the generation of copyWith/== for the entire project
copy_with: false
equal: false
Utilities
IDE Extensions
Freezed extension for VSCode
The Freezed extension might help you work faster with freezed. For example :
- Use Ctrl+Shift+B (Cmd+Shift+B on Mac) to quickly build using
build_runner. - Quickly generate a Freezed class by using Ctrl+Shift+P (Cmd+Shift+P on Mac)>
Generate Freezed class.
Freezed extension for IntelliJ/Android Studio
You can get Live Templates for boiler plate code here.
Example:
- type freezedClass and press Tab to generate a freezed class
@freezed class Demo with _$Demo { } - type freezedFromJson and press Tab to generate the fromJson method for json_serializable
factory Demo.fromJson(Map<String, dynamic> json) => _$DemoFromJson(json);
Linting
You can add freezed specific linting rules that provide helpful utilities and catch common mistakes when creating freezed classes.
Add custom_lint and freezed_lint to your pubspec.yaml:
dart pub add dev:custom_lint
dart pub add dev:freezed_lint
Also add custom_lint to your analysis_options.yaml:
analyzer:
plugins:
- custom_lint
Third-party tools
This part contains community-made tools which integrate with Freezed.
DartJ
DartJ is Flutter application, made by @ttpho, which will generate the Freezed classes from a JSON payload.
Example:
https://github.com/ttpho/ttpho/assets/3994863/5d529258-c02c-4066-925e-ca2ffc68a804
Sponsors
Changelog
3.2.5 - 2026-02-03
Support analyzer 10.0
3.2.4
Support analyzer 9.0.0
3.2.3 - 2025-09-10
Broader the version range for analyzer/source_gen/build
3.2.2 - 2025-09-09
Bump build
3.2.1 - 2025-09-09
Support latest analyzer/source_gen
3.2.0 - 2025-07-18
- Use
build3.0.0-dev. - Use
source_gen3.0.0-dev. - Support Dart 3.8.0 and analyzer 7.5.9 as a minumum.
3.1.0 - 2025-07-02
- Added
when/mapback - Stop writing
// dart format width=80to the generated freezed files.
3.0.6 - 2025-04-05
Remove logs
3.0.5 - 2025-04-05
- Fix ==/hashCode when using inheritance.
The generated ==/hashCode now call
super == otherwhen necessary. - Fix an issue with
DiagnosticableandtoString - Fix ejecting union cases when more than one class is defined in a file
- Fix a copyWith bug when a constructor parameter has no matching property
- Fix null exception in some cases when using generic classes and copyWith
3.0.4 - 2025-03-16
- Update docs (thanks to @lishaduck)
- Support Dart 3.6.0 and analyzer 6.9.0 as a minimum.
3.0.3 - 2025-03-02
Update readme (thanks to @snapsl)
3.0.2 - 2025-02-27
- Fix various deep-copy bugs
- Throw when using
extends/withbut not specifying aMyClass._()constructor - Throw when a Freezed class forgot to include
with _$MyClass - "Normal" classes shouldn't require
MyClass._()when they define getters/methods/...
3.0.1 - 2025-02-27
Fix a bug with diagnosticable objects.
3.0.0 - 2025-02-25
New: Mixed mode
Freezed 3.0 is about supporting a "mixed mode".
From now on, Freezed supports both the usual syntax:
@freezed
sealed class Usual with _$Usual {
factory Usual({int a}) = _Usual;
}
But also:
@freezed
class Usual with _$Usual {
Usual({this.a});
final int a;
}
This has multiple benefits:
- Simple classes don't need Freezed's "weird" syntax and can stay simple
- Unions can keep using the usual
factorysyntax
This also offers a way to use all constructor features, such as initializers
or super():
class Base {
Base(String value);
}
@freezed
class Usual extends Base with _$Usual {
Usual({int? a}) a = a ?? 0, super('value');
final int a;
}
New: Inheritance and non-constant default values.
When using Freezed, a common problem has always been the lack of extends support and non-constant default values.
Besides through "Mixed mode" mentioned above, Freezed now offers a way for factory constructors to specify non-constant defaults and a super(), by
relying on the MyClass._() constructor:
It also has another benefit:
Complex Unions now have a way to use Inheritance and non-constant default values,
by relying on a non-factory MyClass._() constructor.
For context:
Before, when a Freezed class specified a property or method,
it was required to specify a MyClass._() constructor:
@freezed
class Example with _$Example {
// Necessary for helloWorld() to work
Example._();
factory Example(String name) = _Example
void helloWorld() => print('Hello $name');
}
However, that Example._() constructor was required to have no parameter.
Starting Freezed 3.0, this constructor can accept any parameter.
Freezed will then pass its values from other factory constructors, based on name.
In short, this enables extending any class:
class Subclass {
Subclass.name(this.value);
final int value;
}
@freezed
class MyFreezedClass extends Subclass with _$MyFreezedClass {
// We can receive parameters in this constructor, which we can use with `super.field`
MyFreezedClass._(super.value): super.name();
factory MyFreezedClass(int value, /* other fields */) = _MyFreezedClass;
}
It also enables non-constant default values:
@freezed
sealed class Response<T> with _$Response<T> {
// We give "time" parameters a non-constant default
Response._({DateTime? time}) : time = time ?? DateTime.now();
// Constructors may enable passing parameters to ._();
factory Response.data(T value, {DateTime? time}) = ResponseData;
// If ._ parameters are named and optional, factory constructors are not required to specify it
factory Response.error(Object error) = ResponseError;
@override
final DateTime time;
}
New: "Eject" union cases
Along with the mixed mode, it is also possible to eject a "union" case, by having it point to a custom class.
Concretely, you can do:
@freezed
sealed class Result<T> with _$Result {
Result._();
// Data does not exist, so Freezed will generate it as usual
factory Result.data(T data) = ResultData;
// We wrote a ResultError class in the same library, so Freezed won't do anything
factory Result.error(Object error) = ResultError;
}
// We manually wrote `ResultError`
class ResultError<T> extends Result<T> {
ResultError(this.error): super._();
final Object error;
}
This combines nicely with "Mixed mode" mentioned previously, as extracted union cases can also be Freezed classes:
// Using freezed with a simple class:
@freezed
class ResultError<T> extends Result<T> {
ResultError(this.error): super._();
final Object error;
}
// Or using a factory:
@freezed
class ResultError<T> extends Result<T> {
ResultError._(): super._();
factory ResultError(Object error) = _ResultError;
}
This feature offers fine-grained control over every parts of your models.
Note:
Unfortunately, it is kind of required to "extend" the parent class (so here extends Result<T>). This is because Dart doesn't support sealed mixin class, so you can't do
with Result<T> instead.
Other changes:
- Breaking: Removed
map/whenand variants. These have been discouraged since Dart got pattern matching. - Breaking: Freezed classes should now either be
abstract,sealed, or manually implements_$MyClass. - When formatting is disabled (default), Freezed now generates
// dart format off. This prevents having to exclude generated file from formatting check in the CI. - It is now possible to have a private constructor for unions:
@freezed sealed class Result<T> with _$Result { // It wasn't possible to write _data before, but now is. factory Result._data(T data) = ResultData; factory Result.error(Object error) = ResultError; }
2.5.8 - 2025-01-06
Support analyzer 7.0.0
2.5.7 - 2024-07-15
freezed_annotationupgraded to2.4.4
2.5.6 - 2024-07-09
- Generate documentation for
copyWith(thanks to @rekire)
2.5.5 - 2024-07-09
- Stop using
JsonKey(ignore: true)in favour ofJsonKey(includeFromJson: false, includeToJson: false)(thanks to @lrsvmb) - Require json_annotation ^6.8.0
2.5.4 - 2024-07-02
- Require Dart >=3.0.0
- Support latest
collection
2.5.3 - 2024-05-14
- Support analyzer 6.5.0
2.5.2 - 2024-04-11
- Fixed
copyWith(value: null)when using nullable generics
2.5.1 - 2024-04-08
- Fixed
@freezedno-longer working insidepart offiles.
2.5.0
- Added
format: falseflag in thebuild.yaml, to disable formatting in generated files. This can significantly improve performance, but may require updating your CI to not check that generated files are formatted. - Fixed an
InconsistentAnalysisException
2.4.8
- Fixed a performance problem
2.4.7 - 2024-02-04
- Fixed broken link in docs (thanks to @iDevOrz)
2.4.6 - 2023-12-15
- Have the
==implementation useObjectas parameter instead ofdynamic
2.4.5 - 2023-10-10
Loosened analyzer version to support both ^5.13.0 and ^6.0.0 (thanks to @SunlightBro)
2.4.4 - 2023-10-10
Do not throw on unresolved annotations.
2.4.3 - 2023-09-27
- Fix lowercase warning on generated objects.
2.4.2 - 2023-08-03
- Support analyzer 6.0.0
2.4.1 - 2023-07-12
freezed_annotationupgraded to2.3.0
2.4.0 - 2023-07-11
- Allow enabling/disabling all
when/mapvariants at once (thanks to @gaetschwartz)
2.3.5 - 2023-06-05
- Fix regression in analyzer 5.13.0 (thanks to @SunlightBro)
2.3.4 - 2023-05-13
- Fix allow dollar ($) in recursive class names (thanks to @andreasgangso)
- Fix deprecated warning with analyzer 5.12.0 (thanks to @SunlightBro)
- Remove warning when marking a class as
abstract
2.3.3
- Fixed an issue with the generated code when classes contains a
$in their property/constructor/class name (thanks to @medz)
2.3.2
Fix more issues with copyWith
2.3.1
Fix various issues with copyWith
2.3.0
-
Union types now expose properties with the same name but different type on the shared interface. (Thanks to @DevNico)
This allows doing:
@freezed class Union with _$Union { factory Union.first(int value) = _UnionFirst; factory Union.second(double value) = _UnionSecond; } void main() { Union union; num value = union.value; // No need for casting as "num" is the shared interface between int/double } -
Fixes copyWith not working with
nullon some scenarios. -
Fixes a stackoverflow error when continuously passing unmodifiable collections as parameter to freezed classes
-
fix: Deep copy now correctly handles cases where a property is a Freezed class with disabled
copyWith
2.2.1
Upgrade analyzer
2.2.0
- The generated
copyWithis now annotated by@useResult(thanks to @miDeb) - Improved performance of
copyWith(thanks to @miDeb) - Improved type inference when using
mapOrNull/whenOrNull(thanks to @DevNico) - Re-introduced
@With.fromStringand@Implements.fromStringto allow unions to implement generic types. (thanks to @rorystephenson) - fixes @Default for Strings containing (, [ or { (thanks to @hugobrancowb)
- fix Freezed incorrectly comparing primitives using
DeepCollectionEquality(thanks to @knaeckeKami) - fix image links (thanks to @SunlightBro)
2.1.1
- Bump Analyzer to 5.0.0
- Fix an issuee where Freezed fails to detect that the
DiagnosticableAPI is available
2.1.0+1
Improve pub score
2.1.0
- Add support for de/serializing generic Freezed classes (Thanks to @TimWhiting)
- Fixed a StackOverflow error when defining circular
exports (Thanks to @TimWhiting)
2.0.5
Increase minimum analyzer version.
2.0.4
Fixes a bug where using @unfreezed on unions somehow still generated immutable properties.
2.0.3+1
Update warning message when using abstract freezed classes
2.0.3
- fix: performance regression with ==/hashCode/copyWith due to immutable collections (#653)
- fix: build.yaml decoding crash
- fix: remove annotations on internal properties related to immutable collections (#659)
2.0.2
Fixed invalid generated code when defining nullable collections.
2.0.1
- Fixed a bug where the generated when/map methods were potentially invalid when using default values
- Fixed a bug where when/map methods were generated even when not necessary
2.0.0
-
Breaking: freezed_annotation no-longer exports the entire package:collection
-
Breaking: Freezed no-longer generates
$MyClassTearOff. This feature is now available in Dart -
Breaking Removed
@Freezed(maybeMap: )&@Freezed(maybeWhen: )in favor of a separate:@Freezed(map: FreezedMap(...), when: FreezedWhenOptions(...)) -
Breaking: Freezed now converts Lists/Maps/Sets into UnmodifiableListView/UnmodifiableMapView/UnmodifiableSetView. This can be disabled for one class by specifying
@Freezed(makeCollectionsUnmodifiable: false). Alternatively, this can be configured inside thebuild.yamlfile -
Added parameters of
@Freezedto customize the generated code. This includes: fromJson, toJson, map, when, equal, toStringOverride, copyWith.These parameters allow disabling code that would otherwise be generated. It also allows forcing the generation even if it would otherwise not be generated.
-
Feat: Add screaming snake union case type (#617) (thanks to @zbarbuto)
-
Fix null exceptions in some cases when using typedefs (thanks to @smiLLe)
-
Support analyzer 4.0.0
-
Fix an issue with generic Freezed classes failing to compile when using
@With<Mixin<T>>()#614 -
Added
@unfreezedas a variant to@freezed, for mutable classes
1.1.1
- Lints are now disabled inside generated files (requires Dart 2.15)
- Upgraded the analyzer version to 3.0.0
1.1.0
Added support for disabling the generation of maybeMap/maybeWhen (thanks to @Lyokone)
1.0.2+1
Updated the README
1.0.2
Fixed a regression with the ==/hashCode implementation generated for
classes with custom implementations of List/Map/... (see https://github.com/rrousselGit/freezed/issues/561)
1.0.1
Fixed an issue where some build.yaml options were not properly considered.
This includes changing where the generated files should be placed. (Thanks to @sperochon)
1.0.0
Freezed is now stable
This release also:
- reverted an involuntary change of how union types were de/serialized, breaking existing code.
- fixed an issue with generic typedefs (thanks to @SunlightBro #552)
- fixed a potential null exception
0.15.1+1
- Fixed a bug where the generated code for serializable unions with a base class was invalid
0.15.1
- The union key is no longer passed
json_serializablewhen deserializing avoid the case of being anunrecognized_keyin some cases. - Updated dependencies
- Increased minimum Dart SDK required to 2.14.0
- Fixed an issue where classes with a $ in their name could cause Freezed to fail (Thanks to @comigor #542)
- When using typedefs, the generated code now tries to use the typedef too if possible, instead of the aliased type (Thanks to @Norbert515 #536).
- Added support for custom ==/hashCode implementation (Thanks to @casvanluijtelaa #526)
- When writing a custom
toJsonfunction, it will now always take precedence over the default implementation generated by Freezed.
0.15.0+1
Fixed the generated hashCode not compiling for objects with a large number
of properties. (#531)
0.15.0
-
Breaking Changed the syntax for
@Withand@Implementsto use a generic annotation. Before:@With(MyClass) @With.fromString('Generic<int>')After:
@With<MyClass>() @With<Generic<int>>() -
Fixed an issue with fromJson tear-offs not allowing
Object?as map value. (#520) -
Optimized the generated implementation of
hashCodeand== -
Fixed an issue when
MyClass<Object>()andMyClass<int>()could be considered equal -
Require Dart SDK >=2.14.0.
0.14.5
- fixed a bug when using alias imports, potentially using the prefix on every variables.
- When
@Freezed(fallback: '...')is specified and usingfromJson, now support cases where the JSON does not contain the "key".
0.14.4
- Now supports types coming from
import '...' as alias; - generated .freezed.dart are now excluded from code coverage reports.
- if
toStringis overridden, Freezed no-longer appliesDiagnosticablewhich would break thetoString(fixes #221) - Add
union.whenOrNullandunion.mapOrNull, similar tomaybeWhenandmaybeMapbut instead of anorElsethey returnnull.
0.14.3
- Upgraded to support json_serializable 5.0.0
fromJsonnow throwsCheckedFromJsonExceptionif it fails to deserialize an object.- fixed an issue where optional dynamic keys were not allowed.
0.14.2
- Added the ability to specify a fallback constructor when deserializing unions (thanks to @Brazol)
0.14.1+3
Upgrade dependencies
0.14.1+2
Fixed examples in the README
0.14.1+1
- Fixed issues with recursive Freezed classes that could cause the parameter type to be inferred as
dynamic(see #399) - Updated
buildand other similar dependencies to latest
0.14.1
-
Added the ability to customize the JSON value of a union. See https://github.com/rrousselGit/freezed#fromjson---classes-with-multiple-constructors for more information (Thanks to @ookami-kb)
-
Fix an issue where Freezed would not generate code properly if
freezed_annotationwas indirectly imported (by importing a library that exportsfreezed_annotation) (Thanks to @ookami-kb)
0.14.0+2
- Fix
@Assertno-longer working - Fixed an issue where a
factoryusing the=>syntax (so not managed by Freezed) with default values could break code-generation.
0.14.0+1
- fix
sort_unnamed_constructors_firstandcast_nullable_to_non_nullablein the generated code (Thanks to @gaetschwartz #372)
0.14.0
-
Stable null-safety release
-
Upgraded
analyzerto support latest versions -
Freezed classes no-longer need to be abstract.
Before:@freezed abstract class Example with _$Example { factory Example(int value) = _Example; }after:
@freezed class Example with _$Example { factory Example(int value) = _Example; }This leads to better error messages when a Freezed class uses interfaces/mixins but the class is missing a specific property.
-
It is now allowed to add unimplemented getter to Freezed classes. This can be useful to guarantee that union-types all have a common property:
@freezed class Union with _$Union { const factory Union.left(int value) = _Left; const factory Union.right(int value) = _Left; @override int get value; // adding this forces all union cases to possess a `value` property } -
Excluded getters and late variables from the generated
toStringas they could potentially cause a StackOverflow -
Fixed a bug causing properties with annotations to generate the annotations before the `required keyword, leading to a compilation error (see #351).
0.14.0-nullsafety.1
- Upgraded
analyzerto support latest versions
0.14.0-nullsafety.0
-
Freezed classes no-longer need to be abstract.
Before:@freezed abstract class Example with _$Example { factory Example(int value) = _Example; }after:
@freezed class Example with _$Example { factory Example(int value) = _Example; }This leads to better error messages when a Freezed class uses interfaces/mixins but the class is missing a specific property.
-
It is now allowed to add unimplemented getter to Freezed classes. This can be useful to guarantee that union-types all have a common property:
@freezed class Union with _$Union { const factory Union.left(int value) = _Left; const factory Union.right(int value) = _Left; @override int get value; // adding this forces all union cases to possess a `value` property }
0.13.0-nullsafety.2
- Excluded getters and late variables from the generated
toStringas they could potentially cause a StackOverflow
0.13.0-nullsafety.1
- Fixed a bug causing properties with annotations to generate the annotations before the `required keyword, leading to a compilation error (see #351).
0.13.0-nullsafety.0
Migrated to null safety
0.12.7
- fixed a bug where
///above a constructor could cause the generation to stop working (see also https://github.com/rrousselGit/freezed/issues/326)
0.12.6
- Fix Freezed generating code from constructors that do not rely on Freezed (https://github.com/rrousselGit/freezed/issues/323)
0.12.5
- Fixes a bug where comments on named parameters could break generation (https://github.com/rrousselGit/freezed/issues/317)
0.12.4
- fixed a bug that caused the code-generation to never complete (https://github.com/rrousselGit/freezed/issues/312)
0.12.3
- Renamed
Resultin generated code toTResultto avoid naming conflicts - Fixes
InconsistentAnalysisExceptionsometimes being thrown (https://github.com/rrousselGit/freezed/issues/294)
0.12.3-dev
- Renamed
Resultin generated code toTResultto avoid naming conflicts - Fixes
InconsistentAnalysisExceptionsometimes being thrown (https://github.com/rrousselGit/freezed/issues/294)
0.12.2
- Upgrade the dependencies (analyzer, build_runner)
- fixed a bug where a custom
fromJsonconstructor still generated thefromJsonconstructor with json_serializable (see also https://github.com/rrousselGit/freezed/issues/280) - fixed a generation issue when
unionKeycontains special characters (see also https://github.com/rrousselGit/freezed/issues/274)
0.12.1
- Removed dependency on
_fe_analyzer_shared
0.12.0
-
Added
/// @nodocon objects generated by Freezed that starts with$to not pollute dartdoc. -
Added support for documenting properties, by documenting the constructor parameters:
@freezed abstract class Example with _$Example { const factory Example( /// Some documentation String parameter, ) = Person; } -
Added
Assertdecorator to generateassert(...)statements on Freezed classes:@freezed abstract class Person with _$Person { @Assert('name.trim().isNotEmpty', 'name cannot be empty') @Assert('age >= 0') factory Person({ String name, int age, }) = _Person; } -
Now generates a constructor tear-off for
fromJsontoo:@freezed abstract class Person with _$Person { factory Person({ String name, int age}) = _Person; factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); } List<Map<String, dynamic>> list; List<MyClass> persons = list.map($Person.fromJson).toList(); -
Added a way to customize the de/serialization of union types using the
@Freezed(unionKey: 'my-key')decorator.See also https://github.com/rrousselGit/freezed#fromjson---classes-with-multiple-constructors
0.11.6
-
Fixed a bug where deep-copy did not work properly for recursive classes (https://github.com/rrousselGit/freezed/issues/213)
-
Fixed a situation where
@lateproperties could trigger aRangeError(https://github.com/rrousselGit/freezed/issues/219)
0.11.5
- Fixed a bug in which case Freezed generated an invalid
copyWithimplementation
0.11.4
- No-longer generate
when/... for private constructors.
0.11.3
- Fixed a bug where the generated class incorrectly used
Diagnosticableevent ifpackage:flutter/foundationwasn't imported. (Thanks to @avbk)
0.11.2
- Generate
when/map/... even for classes with a single constructors (thanks @andrzejchm)
0.11.1
- Further improve the parsing of recursive freezed class to avoid
dynamics
0.11.0
-
Added
@Withand@Implementsdecorators to allow only a specific constructor of a union type to implement an interface:@freezed abstract class Example with _$Example { const factory Example.person(String name, int age) = Person; @Implements(GeographicArea) const factory Example.city(String name, int population) = City; }Thanks to @long1eu
-
Fixed a bug where a recursive freezed class may generate a property as dynamic (thanks to @maks)
-
Improved the readme (Thanks to @scopendo and @Matsue)
0.10.9
- Fixes a bug where code-generation could end-up in a Stack Overflow if a class depends on itself
0.10.8
- Fix code-generation issue for deep-copy of generic objects (https://github.com/rrousselGit/freezed/issues/128)
0.10.7
- Fixes a bug where classes with both getters and consts constructors caused an exception. (https://github.com/rrousselGit/freezed/issues/142)
0.10.6
- do not create
@requiredannotation for positional arguments onwhen/mapfunctions (thanks to @hpoul) - Fix == returning false for classes with "other" prop (thanks to @mhmdanas)
0.10.5
Fixes classes with getters and a private MyClass._() constructor not properly generating.
0.10.4
Fixed Freezed trying to generate code for factory constructors with a complex body.
0.10.3
- Fixes Freezed trying to generate code for files that don't use it
0.10.2
- Fixes a bug where deep copy did not compile if the class definitions were spread on multiple files.
0.10.1
- Fixed a stack-overflow during generation on recursive classes
- Fixed invalid generated code for classes with a concrete constructor + using Diagnosticable
0.10.0
- Consider optional parameters with a default value as non-nullable
- Add deep-copy support
- Allow the class to define methods/getters
- Do not override
toStringif the user-defined class already overridestoString
0.9.2
Fixes parsing of recursive classes
0.9.1
Support enum and static const default values
0.9.0
Support class-level decorators (see https://github.com/rrousselGit/freezed/issues/50)
0.8.2
Generated file now ignores invalid_override_different_default_values_named
0.8.0
Now also generate constructor tear-off.
0.7.3
Fixes @Default generating invalid code if the default value explicitly used const.
0.7.2
Fix null/bool default value support
0.7.1
Fixes a bug where @Default only worked with strings/numbers/functions/types.
0.7.0
Add support for default values
0.6.3
- Make
freezedignoreuse_function_type_syntax_for_parameterswarnings (see https://github.com/rrousselGit/freezed/issues/57)
0.6.2
- Fixes an issue with inconsistent
hashCodewhen using collections - Fixes a parsing bug with complex concrete factory constructors.
0.6.1
Fixed a bug where @late could incorrectly parse the getter
0.6.0
Add support for cached getters using @late.
0.5.1
- Document
@nullable - fix a bug where static members where not allows (thanks @knaeckeKami)
0.5.0
The generated == now works with collections too.
If a class created has a List/Map/Set/Iterable, then the == will deeply compare these instead of comparing their reference.
0.4.0
Automatically generate assert(property != null) on both constructors and copyWith
methods.
This also adds a @nullable decorator to disable this assertion.
0.3.0
Now use a custom @freezed annotation instead of @immutable.
0.2.5
Fixed a bug where generic json deserialization didn't apply generic parameters (https://github.com/rrousselGit/freezed/issues/32)
0.2.4
Make the generated interface a mixin to fix prefer_mixin lints (https://github.com/rrousselGit/freezed/issues/28)
0.2.3
Fixes a bug where constructors with generic parameters caused the parsing of the redirected constructor to fail. (https://github.com/rrousselGit/freezed/issues/25)
0.2.2
Fixes a bug where the code would not compile if a property had the same name as a named constructor.
0.2.1
Fixes a bug where classes with a single constructor + fromJson did not generate properties/copyWith.
0.2.0
Transfer all parameters decorators to the generated properties.
0.1.4
- Fixed a bug where
map/maybeMapon generic classes didn't pass the generic parameters.
0.1.3+1
- Fix README's index
0.1.3
- Don't generate anything for factory constructors with a body. (https://github.com/rrousselGit/freezed/issues/9)
0.1.2
-
fromJson/toJsonno longer require/add aruntimeTypekey for classes with a single constructor. (https://github.com/rrousselGit/freezed/issues/7) -
don't generate anything for classes that add
@freezedbut define real properties (https://github.com/rrousselGit/freezed/issues/5)
0.1.1
Upgrade min range of analyzer dependency
0.1.0
Add support for json_serializable
0.0.2
Implicitly generate debugFillProperties if the necessary classes are imported.
0.0.1
Add generic support
0.0.0
Initial release



