ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0.1 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://pub.dev/packages/freezed |
| Last Crawled | 2026-04-09 10:12:32 (1 day ago) |
| First Indexed | 2020-02-10 16:29:21 (6 years ago) |
| HTTP Status Code | 200 |
| Meta Title | freezed | Dart package |
| Meta Description | Code generation for immutable classes that has a simple syntax/API without compromising on the features. |
| Meta Canonical | null |
| Boilerpipe Text | English
|
한국어
|
简体中文
|
日本語
|
Tiếng Việt
Welcome to
Freezed
, yet another code generator for data classes, tagged unions, nested classes and cloning.
To migrate from 2.0.0 to 3.0.0, see
changelog
and our
migration guide
.
Dart is awesome, but defining a "model" can be tedious. You have to:
Define a constructor + properties
Override
toString
,
operator ==
,
hashCode
Implement a
copyWith
method 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
Migration to 3.0.0
Motivation
Index
How to use
Install
Disabling invalid_annotation_target warning and warning in generates files
Run the generator
Creating a Model using Freezed
Primary constructors
Adding getters and methods to our models
Asserts
Default values
Non-constant default values
Extending classes
Defining a mutable class instead of an immutable one
Allowing the mutation of Lists/Maps/Sets
Classic classes
How copyWith works
Going further: Deep copy
Decorators and comments
FromJson/ToJson
fromJSON - classes with multiple constructors
Deserializing generic classes
Union types
Shared properties
Using pattern matching to read non-shared properties
Mixins and Interfaces for individual classes for union types
Ejecting an individual union case
(Legacy) Pattern matching utilities
When
Map
Configurations
Changing the behavior for a specific model
Changing the behavior for the entire project
Utilities
IDE Extensions
Freezed extension for VSCode
Freezed extension for IntelliJ/Android Studio
Linting
Third-party tools
DartJ
Sponsors
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
copied to clipboard
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
copied to clipboard
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
copied to clipboard
To run the code generator, execute the following command:
dart run build_runner watch -d
copied to clipboard
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'
;
copied to clipboard
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.
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
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);
}
copied to clipboard
The following snippet defines a model named
Person
:
Person
has 3 properties:
firstName
,
lastName
and
age
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 a
toJson
method for us.
Freezed will also automatically generate:
a
copyWith
method, for cloning the object with different properties
a
toString
override listing all the properties of the object
an
operator ==
and
hashCode
override (since
Person
is immutable)
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
factory
keyword
as showcased (
const
is 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'
);
}
}
copied to clipboard
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'
);
}
}
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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);
}
copied to clipboard
This defines a model mostly identical to our previous snippets, but with the following
differences:
firstName
and
lastName
are now mutable. As such, we can write:
void
main() {
var
person = Person(firstName:
'John'
, lastName:
'Smith'
, age:
42
);
person.firstName =
'Mona'
;
person.lastName =
'Lisa'
;
}
copied to clipboard
age
is still immutable, because we explicitly marked the property as
final
.
Person
no-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
}
copied to clipboard
Of course, since our
Person
class is mutable, it is no-longer possible
to instantiate it using
const
.
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
}
copied to clipboard
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
}
copied to clipboard
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
);
}
copied to clipboard
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.
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;
}
copied to clipboard
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)
}
copied to clipboard
Notice Freezed supports
person.copyWith(age: null)
.
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;
}
copied to clipboard
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'
,
),
),
);
copied to clipboard
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'
);
copied to clipboard
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'
);
copied to clipboard
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(...));
copied to clipboard
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'
);
copied to clipboard
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'
);
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
This will deprecate both:
The constructor
Person(gender: Gender.something);
// gender is deprecated
copied to clipboard
The generated class's constructor:
_Person(gender: Gender.something);
// gender is deprecated
copied to clipboard
the property:
Person person;
print
(person.gender);
// gender is deprecated
copied to clipboard
the
copyWith
parameter:
Person person;
person.copyWith(gender: Gender.something);
// gender is deprecated
copied to clipboard
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;
}
copied to clipboard
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;
}
copied to clipboard
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);
}
copied to clipboard
Don't forget to add
json_serializable
to your
pubspec.yaml
file:
dev_dependencies:
json_serializable:
copied to clipboard
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
=>
.
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);
}
copied to clipboard
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()"
}
]
copied to clipboard
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);
}
copied to clipboard
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()"
}
]
copied to clipboard
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
copied to clipboard
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();
}
copied to clipboard
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);
}
copied to clipboard
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);
}
copied to clipboard
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
).
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);
}
copied to clipboard
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
copied to clipboard
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);
}
copied to clipboard
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);
}
copied to clipboard
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
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;
}
copied to clipboard
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
}
copied to clipboard
We'll see why in the following section.
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;
}
copied to clipboard
Then you will be unable to read
age
and
population
directly:
var
example = Example.person(
'Remi'
,
24
);
print
(example.age);
// does not compile!
copied to clipboard
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
copied to clipboard
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)
copied to clipboard
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
copied to clipboard
To solve this problem, we need check the state of our object using what we call "pattern matching".
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;
}
copied to clipboard
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
)'
),
}
copied to clipboard
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
)'
);
}
copied to clipboard
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;
}
copied to clipboard
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>;
}
copied to clipboard
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.
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;
}
copied to clipboard
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>
}
copied to clipboard
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
._();
}
copied to clipboard
(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
/
map
are kept for users who have yet to
migrate to Dart 3.
But in the long term, you should stop relying on them and migrate to
switch
expressions.
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;
}
copied to clipboard
Then [when] will be:
var
union = Union(
42
);
print
(
union.
when
(
(
int
value) =>
'Data
$value
'
,
loading: () =>
'loading'
,
error: (
String?
message) =>
'Error:
$message
'
,
),
);
// Data 42
copied to clipboard
Whereas if we defined:
@freezed
sealed
class
Model
with
_
$
Model
{
factory
Model.first(
String
a) = First;
factory
Model.second(
int
b,
bool
c) = Second;
}
copied to clipboard
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
copied to clipboard
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;
}
copied to clipboard
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
copied to clipboard
[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
copied to clipboard
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)
copied to clipboard
Freezed offers various options to customize the generated code. To do so, there are two possibilities:
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;
}
copied to clipboard
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
{...}
copied to clipboard
To view all the possibilities, see the documentation of
@Freezed
:
https://pub.dev/documentation/freezed_annotation/latest/freezed_annotation/Freezed-class.html
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/
copied to clipboard
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
copied to clipboard
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
{
}
copied to clipboard
type
freezedFromJson
and press
Tab
to generate the fromJson method for json_serializable
factory
Demo.fromJson(
Map
<
String
,
dynamic
> json) => _$DemoFromJson(json);
copied to clipboard
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
copied to clipboard
Also add
custom_lint
to your
analysis_options.yaml
:
analyzer:
plugins:
-
custom_lint
copied to clipboard
This part contains community-made tools which integrate with Freezed.
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 |
| Markdown | [](https://pub.dev/)
Sign in
Help
### pub.dev
[Searching for packages](https://pub.dev/help/search)[Package scoring and pub points](https://pub.dev/help/scoring)
### Flutter
[Using packages](https://flutter.dev/using-packages/)[Developing packages and plugins](https://flutter.dev/developing-packages/)[Publishing a package](https://dart.dev/tools/pub/publishing)
### Dart
[Using packages](https://dart.dev/guides/packages)[Publishing a package](https://dart.dev/tools/pub/publishing)
### pub.dev 
[Searching for packages](https://pub.dev/help/search)[Package scoring and pub points](https://pub.dev/help/scoring)
### Flutter 
[Using packages](https://flutter.dev/using-packages/)[Developing packages and plugins](https://flutter.dev/developing-packages/)[Publishing a package](https://dart.dev/tools/pub/publishing)
### Dart 
[Using packages](https://dart.dev/guides/packages)[Publishing a package](https://dart.dev/tools/pub/publishing)
[](https://flutter.dev/docs/development/packages-and-plugins/favorites "Package is a Flutter Favorite")
# freezed 3.2.5  freezed: ^3.2.5 copied to clipboard
Published [2 months ago](https://pub.dev/packages/freezed "Feb 3, 2026") • [dash-overflow.net](https://pub.dev/publishers/dash-overflow.net)
SDK[Flutter](https://pub.dev/packages?q=sdk%3Aflutter "Packages compatible with Flutter SDK")
Platform[Android](https://pub.dev/packages?q=platform%3Aandroid "Packages compatible with Android platform")[iOS](https://pub.dev/packages?q=platform%3Aios "Packages compatible with iOS platform")[Linux](https://pub.dev/packages?q=platform%3Alinux "Packages compatible with Linux platform")[macOS](https://pub.dev/packages?q=platform%3Amacos "Packages compatible with macOS platform")[Windows](https://pub.dev/packages?q=platform%3Awindows "Packages compatible with Windows platform")

4\.4k
→
### Metadata
Code generation for immutable classes that has a simple syntax/API without compromising on the features.
[More...]()
- Readme
- [Changelog](https://pub.dev/packages/freezed/changelog)
- [Example](https://pub.dev/packages/freezed/example)
- [Installing](https://pub.dev/packages/freezed/install)
- [Versions](https://pub.dev/packages/freezed/versions)
- [Scores](https://pub.dev/packages/freezed/score)
[English](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/README.md) \| [한국어](https://github.com/rrousselGit/freezed/blob/master/resources/translations/ko_KR/README.md) \| [简体中文](https://github.com/rrousselGit/freezed/blob/master/resources/translations/zh_CN/README.md) \| [日本語](https://github.com/rrousselGit/freezed/blob/master/resources/translations/ja_JP/README.md) \| [Tiếng Việt](https://github.com/rrousselGit/freezed/blob/master/resources/translations/vi_VN/README.md)
 [](https://pub.dartlang.org/packages/freezed) [](https://discord.gg/GSt793j6eT)
[](https://flutter.dev/docs/development/packages-and-plugins/favorites)
Welcome to [Freezed](https://pub.dartlang.org/packages/freezed), yet another code generator for data classes, tagged unions, nested classes and cloning.
# Migration to 3.0.0 [\#](https://pub.dev/packages/freezed#migration-to-300)
To migrate from 2.0.0 to 3.0.0, see [changelog](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/CHANGELOG.md#300---2025-02-25) and our [migration guide](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/migration_guide.md).
# Motivation [\#](https://pub.dev/packages/freezed#motivation)
Dart is awesome, but defining a "model" can be tedious. You have to:
- Define a constructor + properties
- Override `toString`, `operator ==`, `hashCode`
- Implement a `copyWith` method 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 [\#](https://pub.dev/packages/freezed#index)
- [Migration to 3.0.0](https://pub.dev/packages/freezed#migration-to-300)
- [Motivation](https://pub.dev/packages/freezed#motivation)
- [Index](https://pub.dev/packages/freezed#index)
- [How to use](https://pub.dev/packages/freezed#how-to-use)
- [Install](https://pub.dev/packages/freezed#install)
- [Disabling invalid\_annotation\_target warning and warning in generates files](https://pub.dev/packages/freezed#disabling-invalid_annotation_target-warning-and-warning-in-generates-files)
- [Run the generator](https://pub.dev/packages/freezed#run-the-generator)
- [Creating a Model using Freezed](https://pub.dev/packages/freezed#creating-a-model-using-freezed)
- [Primary constructors](https://pub.dev/packages/freezed#primary-constructors)
- [Adding getters and methods to our models](https://pub.dev/packages/freezed#adding-getters-and-methods-to-our-models)
- [Asserts](https://pub.dev/packages/freezed#asserts)
- [Default values](https://pub.dev/packages/freezed#default-values)
- [Non-constant default values](https://pub.dev/packages/freezed#non-constant-default-values)
- [Extending classes](https://pub.dev/packages/freezed#extending-classes)
- [Defining a mutable class instead of an immutable one](https://pub.dev/packages/freezed#defining-a-mutable-class-instead-of-an-immutable-one)
- [Allowing the mutation of Lists/Maps/Sets](https://pub.dev/packages/freezed#allowing-the-mutation-of-listsmapssets)
- [Classic classes](https://pub.dev/packages/freezed#classic-classes)
- [How copyWith works](https://pub.dev/packages/freezed#how-copywith-works)
- [Going further: Deep copy](https://pub.dev/packages/freezed#going-further-deep-copy)
- [Decorators and comments](https://pub.dev/packages/freezed#decorators-and-comments)
- [FromJson/ToJson](https://pub.dev/packages/freezed#fromjsontojson)
- [fromJSON - classes with multiple constructors](https://pub.dev/packages/freezed#fromjson---classes-with-multiple-constructors)
- [Deserializing generic classes](https://pub.dev/packages/freezed#deserializing-generic-classes)
- [Union types](https://pub.dev/packages/freezed#union-types)
- [Shared properties](https://pub.dev/packages/freezed#shared-properties)
- [Using pattern matching to read non-shared properties](https://pub.dev/packages/freezed#using-pattern-matching-to-read-non-shared-properties)
- [Mixins and Interfaces for individual classes for union types](https://pub.dev/packages/freezed#mixins-and-interfaces-for-individual-classes-for-union-types)
- [Ejecting an individual union case](https://pub.dev/packages/freezed#ejecting-an-individual-union-case)
- [(Legacy) Pattern matching utilities](https://pub.dev/packages/freezed#legacy-pattern-matching-utilities)
- [When](https://pub.dev/packages/freezed#when)
- [Map](https://pub.dev/packages/freezed#map)
- [Configurations](https://pub.dev/packages/freezed#configurations)
- [Changing the behavior for a specific model](https://pub.dev/packages/freezed#changing-the-behavior-for-a-specific-model)
- [Changing the behavior for the entire project](https://pub.dev/packages/freezed#changing-the-behavior-for-the-entire-project)
- [Utilities](https://pub.dev/packages/freezed#utilities)
- [IDE Extensions](https://pub.dev/packages/freezed#ide-extensions)
- [Freezed extension for VSCode](https://pub.dev/packages/freezed#freezed-extension-for-vscode)
- [Freezed extension for IntelliJ/Android Studio](https://pub.dev/packages/freezed#freezed-extension-for-intellijandroid-studio)
- [Linting](https://pub.dev/packages/freezed#linting)
- [Third-party tools](https://pub.dev/packages/freezed#third-party-tools)
- [DartJ](https://pub.dev/packages/freezed#dartj)
- [Sponsors](https://pub.dev/packages/freezed#sponsors)
# How to use [\#](https://pub.dev/packages/freezed#how-to-use)
## Install [\#](https://pub.dev/packages/freezed#install)
To use [Freezed](https://pub.dartlang.org/packages/freezed), you will need your typical [build\_runner](https://pub.dev/packages/build_runner)/code-generator setup.
First, install [build\_runner](https://pub.dev/packages/build_runner) and [Freezed](https://pub.dartlang.org/packages/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
```
copied to clipboard
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
```
copied to clipboard
This installs three packages:
- [build\_runner](https://pub.dev/packages/build_runner), the tool to run code-generators
- [freezed](https://pub.dartlang.org/packages/freezed), the code generator
- [freezed\_annotation](https://pub.dev/packages/freezed_annotation), a package containing annotations for [freezed](https://pub.dartlang.org/packages/freezed).
### Disabling invalid\_annotation\_target warning and warning in generates files [\#](https://pub.dev/packages/freezed#disabling-invalid_annotation_target-warning-and-warning-in-generates-files)
If you plan on using [Freezed](https://pub.dartlang.org/packages/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
```
copied to clipboard
## Run the generator [\#](https://pub.dev/packages/freezed#run-the-generator)
To run the code generator, execute the following command:
```
dart run build_runner watch -d
```
copied to clipboard
Note that like most code-generators, [Freezed](https://pub.dartlang.org/packages/freezed) will need you to both import the annotation ([freezed\_annotation](https://pub.dartlang.org/packages/freezed_annotation)) and use the `part` keyword on the top of your files.
As such, a file that wants to use [Freezed](https://pub.dartlang.org/packages/freezed) will start with:
```
import 'package:freezed_annotation/freezed_annotation.dart';
part 'my_file.freezed.dart';
```
copied to clipboard
**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](https://pub.dartlang.org/packages/freezed) will automatically do it for you.
## Creating a Model using Freezed [\#](https://pub.dev/packages/freezed#creating-a-model-using-freezed)
Freezed offers two ways of creating data-classes:
- [Primary constructors](https://pub.dev/packages/freezed#primary-constructors) ; where you define a constructor and Freezed generates the associated fields. This is simulating the [Primary Constructor](https://github.com/dart-lang/language/issues/2364) using `factory`.
- [Classic classes](https://pub.dev/packages/freezed#classic-classes), where you write a normal Dart class and Freezed only handles `toString/==/copyWith`
### Primary constructors [\#](https://pub.dev/packages/freezed#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);
}
```
copied to clipboard
The following snippet defines a model named `Person`:
- `Person` has 3 properties: `firstName`, `lastName` and `age`
- 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 a `toJson` method for us.
- Freezed will also automatically generate:
- a `copyWith` method, for cloning the object with different properties
- a `toString` override listing all the properties of the object
- an `operator ==` and `hashCode` override (since `Person` is immutable)
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 `factory` keyword as showcased (`const` is 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');
}
}
```
copied to clipboard
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');
}
}
```
copied to clipboard
#### 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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
#### 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;
}
```
copied to clipboard
**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](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
This syntax gives full control over inheritance.
Of course, you can also opt-out of `factory` constructors and write normal classes. See [Classic Classes](https://pub.dev/packages/freezed#classic-classes).
In general, this workaround makes more sense for [Unions](https://pub.dev/packages/freezed#union-types), 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);
}
```
copied to clipboard
This defines a model mostly identical to our previous snippets, but with the following differences:
- `firstName` and `lastName` are now mutable. As such, we can write:
```
void main() {
var person = Person(firstName: 'John', lastName: 'Smith', age: 42);
person.firstName = 'Mona';
person.lastName = 'Lisa';
}
```
copied to clipboard
- `age` is still immutable, because we explicitly marked the property as `final`.
- `Person` no-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
}
```
copied to clipboard
- Of course, since our `Person` class is mutable, it is no-longer possible to instantiate it using `const`.
#### 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
}
```
copied to clipboard
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
}
```
copied to clipboard
### Classic classes [\#](https://pub.dev/packages/freezed#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);
}
```
copied to clipboard
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 [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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)
}
```
copied to clipboard
Notice Freezed supports `person.copyWith(age: null)`.
### Going further: Deep copy [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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',
),
),
);
```
copied to clipboard
This *works*, but is relatively verbose with a lot of duplicates.
This is where we could use [Freezed](https://pub.dartlang.org/packages/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');
```
copied to clipboard
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');
```
copied to clipboard
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(...));
```
copied to clipboard
**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');
```
copied to clipboard
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');
```
copied to clipboard
## Decorators and comments [\#](https://pub.dev/packages/freezed#decorators-and-comments)
[Freezed](https://pub.dartlang.org/packages/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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
This will deprecate both:
- The constructor
```
Person(gender: Gender.something); // gender is deprecated
```
copied to clipboard
- The generated class's constructor:
```
_Person(gender: Gender.something); // gender is deprecated
```
copied to clipboard
- the property:
```
Person person;
print(person.gender); // gender is deprecated
```
copied to clipboard
- the `copyWith` parameter:
```
Person person;
person.copyWith(gender: Gender.something); // gender is deprecated
```
copied to clipboard
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;
}
```
copied to clipboard
## FromJson/ToJson [\#](https://pub.dev/packages/freezed#fromjsontojson)
While [Freezed](https://pub.dartlang.org/packages/freezed) will not generate your typical `fromJson`/`toJson` by itself, it knows what [json\_serializable](https://pub.dev/packages/json_serializable) is.
Making a class compatible with [json\_serializable](https://pub.dev/packages/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;
}
```
copied to clipboard
The changes necessary to make it compatible with [json\_serializable](https://pub.dev/packages/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);
}
```
copied to clipboard
Don't forget to add `json_serializable` to your `pubspec.yaml` file:
```
dev_dependencies:
json_serializable:
```
copied to clipboard
That's it\!
With these changes, [Freezed](https://pub.dartlang.org/packages/freezed) will automatically ask [json\_serializable](https://pub.dev/packages/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 [\#](https://pub.dev/packages/freezed#fromjson---classes-with-multiple-constructors)
For classes with multiple constructors, [Freezed](https://pub.dartlang.org/packages/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);
}
```
copied to clipboard
Then [Freezed](https://pub.dartlang.org/packages/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()"
}
]
```
copied to clipboard
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);
}
```
copied to clipboard
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()"
}
]
```
copied to clipboard
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
```
copied to clipboard
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();
}
```
copied to clipboard
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);
}
```
copied to clipboard
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);
}
```
copied to clipboard
**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](https://github.com/google/json_serializable.dart/tree/master/json_serializable#build-configuration)).
### Deserializing generic classes [\#](https://pub.dev/packages/freezed#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);
}
```
copied to clipboard
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
```
copied to clipboard
**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);
}
```
copied to clipboard
**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);
}
```
copied to clipboard
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](https://pub.dev/packages/json_serializable#build-configuration).
See also the [decorators](https://pub.dev/packages/freezed#decorators-and-comments) section
## Union types [\#](https://pub.dev/packages/freezed#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](https://pub.dartlang.org/packages/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;
}
```
copied to clipboard
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
}
```
copied to clipboard
We'll see why in the following section.
### Shared properties [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
Then you will be unable to read `age` and `population` directly:
```
var example = Example.person('Remi', 24);
print(example.age); // does not compile!
```
copied to clipboard
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
```
copied to clipboard
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)
```
copied to clipboard
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
```
copied to clipboard
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 [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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)'),
}
```
copied to clipboard
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)');
}
```
copied to clipboard
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 [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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>;
}
```
copied to clipboard
**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](https://pub.dev/packages/freezed#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 [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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>
}
```
copied to clipboard
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._();
}
```
copied to clipboard
#### (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`/`map` are kept for users who have yet to migrate to Dart 3. But in the long term, you should stop relying on them and migrate to `switch` expressions.
##### 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;
}
```
copied to clipboard
Then \[when\] will be:
```
var union = Union(42);
print(
union.when(
(int value) => 'Data $value',
loading: () => 'loading',
error: (String? message) => 'Error: $message',
),
); // Data 42
```
copied to clipboard
Whereas if we defined:
```
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
}
```
copied to clipboard
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
```
copied to clipboard
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;
}
```
copied to clipboard
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
```
copied to clipboard
\[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
```
copied to clipboard
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)
```
copied to clipboard
## Configurations [\#](https://pub.dev/packages/freezed#configurations)
Freezed offers various options to customize the generated code. To do so, there are two possibilities:
### Changing the behavior for a specific model [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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 {...}
```
copied to clipboard
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 [\#](https://pub.dev/packages/freezed#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/
```
copied to clipboard
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
```
copied to clipboard
# Utilities [\#](https://pub.dev/packages/freezed#utilities)
## IDE Extensions [\#](https://pub.dev/packages/freezed#ide-extensions)
### Freezed extension for VSCode [\#](https://pub.dev/packages/freezed#freezed-extension-for-vscode)
The [Freezed](https://marketplace.visualstudio.com/items?itemName=blaxou.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 [\#](https://pub.dev/packages/freezed#freezed-extension-for-intellijandroid-studio)
You can get Live Templates for boiler plate code [here](https://github.com/Tinhorn/freezed_intellij_live_templates).
Example:
- type **freezedClass** and press `Tab` to generate a freezed class
```
@freezed
class Demo with _$Demo {
}
```
copied to clipboard
- type **freezedFromJson** and press `Tab` to generate the fromJson method for json\_serializable
```
factory Demo.fromJson(Map<String, dynamic> json) => _$DemoFromJson(json);
```
copied to clipboard
## Linting [\#](https://pub.dev/packages/freezed#linting)
You can add `freezed` specific linting rules that provide helpful utilities and catch common mistakes when creating `freezed` classes.
Add [`custom_lint`](https://pub.dev/packages/custom_lint) and `freezed_lint` to your `pubspec.yaml`:
```
dart pub add dev:custom_lint
dart pub add dev:freezed_lint
```
copied to clipboard
Also add `custom_lint` to your `analysis_options.yaml`:
```
analyzer:
plugins:
- custom_lint
```
copied to clipboard
## Third-party tools [\#](https://pub.dev/packages/freezed#third-party-tools)
This part contains community-made tools which integrate with Freezed.
### DartJ [\#](https://pub.dev/packages/freezed#dartj)
[DartJ](https://dartj.web.app/#/) is Flutter application, made by [**@ttpho**](https://github.com/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 [\#](https://pub.dev/packages/freezed#sponsors)
[](https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg)
[4\.47k likes140 points1.87M downloads](https://pub.dev/packages/freezed/score)
### Documentation
[API reference](https://pub.dev/documentation/freezed/latest/)
### Publisher
[dash-overflow.net](https://pub.dev/publishers/dash-overflow.net)
### Weekly Downloads
2025\.05.13 - 2026.04.07
### Metadata
Code generation for immutable classes that has a simple syntax/API without compromising on the features.
[Repository (GitHub)](https://github.com/rrousselGit/freezed)
[View/report issues](https://github.com/rrousselGit/freezed/issues)
### License
MIT ([license](https://pub.dev/packages/freezed/license))
### Dependencies
[analyzer](https://pub.dev/packages/analyzer ">=9.0.0 <11.0.0"), [build](https://pub.dev/packages/build ">=3.0.0 <5.0.0"), [build\_config](https://pub.dev/packages/build_config "^1.1.0"), [collection](https://pub.dev/packages/collection "^1.15.0"), [dart\_style](https://pub.dev/packages/dart_style "^3.0.0"), [freezed\_annotation](https://pub.dev/packages/freezed_annotation "3.1.0"), [json\_annotation](https://pub.dev/packages/json_annotation "^4.8.0"), [meta](https://pub.dev/packages/meta "^1.9.1"), [pub\_semver](https://pub.dev/packages/pub_semver "^2.2.0"), [source\_gen](https://pub.dev/packages/source_gen ">=3.0.0 <5.0.0")
### More
[Packages that depend on freezed](https://pub.dev/packages?q=dependency%3Afreezed)
### ← Metadata
[4\.47k likes140 points1.87M downloads](https://pub.dev/packages/freezed/score)
### Documentation
[API reference](https://pub.dev/documentation/freezed/latest/)
### Publisher
[dash-overflow.net](https://pub.dev/publishers/dash-overflow.net)
### Weekly Downloads
2025\.05.13 - 2026.04.07
### Metadata
Code generation for immutable classes that has a simple syntax/API without compromising on the features.
[Repository (GitHub)](https://github.com/rrousselGit/freezed)
[View/report issues](https://github.com/rrousselGit/freezed/issues)
### License
MIT ([license](https://pub.dev/packages/freezed/license))
### Dependencies
[analyzer](https://pub.dev/packages/analyzer ">=9.0.0 <11.0.0"), [build](https://pub.dev/packages/build ">=3.0.0 <5.0.0"), [build\_config](https://pub.dev/packages/build_config "^1.1.0"), [collection](https://pub.dev/packages/collection "^1.15.0"), [dart\_style](https://pub.dev/packages/dart_style "^3.0.0"), [freezed\_annotation](https://pub.dev/packages/freezed_annotation "3.1.0"), [json\_annotation](https://pub.dev/packages/json_annotation "^4.8.0"), [meta](https://pub.dev/packages/meta "^1.9.1"), [pub\_semver](https://pub.dev/packages/pub_semver "^2.2.0"), [source\_gen](https://pub.dev/packages/source_gen ">=3.0.0 <5.0.0")
### More
[Packages that depend on freezed](https://pub.dev/packages?q=dependency%3Afreezed)
[Back]()

![]()

[Dart language](https://dart.dev/)[Report package](https://pub.dev/report?subject=package%3Afreezed&url=https%3A%2F%2Fpub.dev%2Fpackages%2Ffreezed)[Policy](https://pub.dev/policy)[Terms](https://www.google.com/intl/en/policies/terms/)[API Terms](https://developers.google.com/terms/)[Security](https://pub.dev/security)[Privacy](https://www.google.com/intl/en/policies/privacy/)[Help](https://pub.dev/help)[](https://pub.dev/feed.atom)[](https://github.com/dart-lang/pub-dev/issues/new?body=URL%3A+https%3A%2F%2Fpub.dev%2Fpackages%2Ffreezed%0A%0A%3CDescribe+your+issue+or+suggestion+here%3E&title=%3CSummarize+your+issues+here%3E&labels=Area%3A+site+feedback) |
| Readable Markdown | [English](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/README.md) \| [한국어](https://github.com/rrousselGit/freezed/blob/master/resources/translations/ko_KR/README.md) \| [简体中文](https://github.com/rrousselGit/freezed/blob/master/resources/translations/zh_CN/README.md) \| [日本語](https://github.com/rrousselGit/freezed/blob/master/resources/translations/ja_JP/README.md) \| [Tiếng Việt](https://github.com/rrousselGit/freezed/blob/master/resources/translations/vi_VN/README.md)
 [](https://pub.dartlang.org/packages/freezed) [](https://discord.gg/GSt793j6eT)
[](https://flutter.dev/docs/development/packages-and-plugins/favorites)
Welcome to [Freezed](https://pub.dartlang.org/packages/freezed), yet another code generator for data classes, tagged unions, nested classes and cloning.
To migrate from 2.0.0 to 3.0.0, see [changelog](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/CHANGELOG.md#300---2025-02-25) and our [migration guide](https://github.com/rrousselGit/freezed/blob/master/packages/freezed/migration_guide.md).
Dart is awesome, but defining a "model" can be tedious. You have to:
- Define a constructor + properties
- Override `toString`, `operator ==`, `hashCode`
- Implement a `copyWith` method 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 |
|---|---|
|  |  |
- [Migration to 3.0.0](https://pub.dev/packages/freezed#migration-to-300)
- [Motivation](https://pub.dev/packages/freezed#motivation)
- [Index](https://pub.dev/packages/freezed#index)
- [How to use](https://pub.dev/packages/freezed#how-to-use)
- [Install](https://pub.dev/packages/freezed#install)
- [Disabling invalid\_annotation\_target warning and warning in generates files](https://pub.dev/packages/freezed#disabling-invalid_annotation_target-warning-and-warning-in-generates-files)
- [Run the generator](https://pub.dev/packages/freezed#run-the-generator)
- [Creating a Model using Freezed](https://pub.dev/packages/freezed#creating-a-model-using-freezed)
- [Primary constructors](https://pub.dev/packages/freezed#primary-constructors)
- [Adding getters and methods to our models](https://pub.dev/packages/freezed#adding-getters-and-methods-to-our-models)
- [Asserts](https://pub.dev/packages/freezed#asserts)
- [Default values](https://pub.dev/packages/freezed#default-values)
- [Non-constant default values](https://pub.dev/packages/freezed#non-constant-default-values)
- [Extending classes](https://pub.dev/packages/freezed#extending-classes)
- [Defining a mutable class instead of an immutable one](https://pub.dev/packages/freezed#defining-a-mutable-class-instead-of-an-immutable-one)
- [Allowing the mutation of Lists/Maps/Sets](https://pub.dev/packages/freezed#allowing-the-mutation-of-listsmapssets)
- [Classic classes](https://pub.dev/packages/freezed#classic-classes)
- [How copyWith works](https://pub.dev/packages/freezed#how-copywith-works)
- [Going further: Deep copy](https://pub.dev/packages/freezed#going-further-deep-copy)
- [Decorators and comments](https://pub.dev/packages/freezed#decorators-and-comments)
- [FromJson/ToJson](https://pub.dev/packages/freezed#fromjsontojson)
- [fromJSON - classes with multiple constructors](https://pub.dev/packages/freezed#fromjson---classes-with-multiple-constructors)
- [Deserializing generic classes](https://pub.dev/packages/freezed#deserializing-generic-classes)
- [Union types](https://pub.dev/packages/freezed#union-types)
- [Shared properties](https://pub.dev/packages/freezed#shared-properties)
- [Using pattern matching to read non-shared properties](https://pub.dev/packages/freezed#using-pattern-matching-to-read-non-shared-properties)
- [Mixins and Interfaces for individual classes for union types](https://pub.dev/packages/freezed#mixins-and-interfaces-for-individual-classes-for-union-types)
- [Ejecting an individual union case](https://pub.dev/packages/freezed#ejecting-an-individual-union-case)
- [(Legacy) Pattern matching utilities](https://pub.dev/packages/freezed#legacy-pattern-matching-utilities)
- [When](https://pub.dev/packages/freezed#when)
- [Map](https://pub.dev/packages/freezed#map)
- [Configurations](https://pub.dev/packages/freezed#configurations)
- [Changing the behavior for a specific model](https://pub.dev/packages/freezed#changing-the-behavior-for-a-specific-model)
- [Changing the behavior for the entire project](https://pub.dev/packages/freezed#changing-the-behavior-for-the-entire-project)
- [Utilities](https://pub.dev/packages/freezed#utilities)
- [IDE Extensions](https://pub.dev/packages/freezed#ide-extensions)
- [Freezed extension for VSCode](https://pub.dev/packages/freezed#freezed-extension-for-vscode)
- [Freezed extension for IntelliJ/Android Studio](https://pub.dev/packages/freezed#freezed-extension-for-intellijandroid-studio)
- [Linting](https://pub.dev/packages/freezed#linting)
- [Third-party tools](https://pub.dev/packages/freezed#third-party-tools)
- [DartJ](https://pub.dev/packages/freezed#dartj)
- [Sponsors](https://pub.dev/packages/freezed#sponsors)
To use [Freezed](https://pub.dartlang.org/packages/freezed), you will need your typical [build\_runner](https://pub.dev/packages/build_runner)/code-generator setup.
First, install [build\_runner](https://pub.dev/packages/build_runner) and [Freezed](https://pub.dartlang.org/packages/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
```
copied to clipboard
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
```
copied to clipboard
This installs three packages:
- [build\_runner](https://pub.dev/packages/build_runner), the tool to run code-generators
- [freezed](https://pub.dartlang.org/packages/freezed), the code generator
- [freezed\_annotation](https://pub.dev/packages/freezed_annotation), a package containing annotations for [freezed](https://pub.dartlang.org/packages/freezed).
### Disabling invalid\_annotation\_target warning and warning in generates files [\#](https://pub.dev/packages/freezed#disabling-invalid_annotation_target-warning-and-warning-in-generates-files)
If you plan on using [Freezed](https://pub.dartlang.org/packages/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
```
copied to clipboard
To run the code generator, execute the following command:
```
dart run build_runner watch -d
```
copied to clipboard
Note that like most code-generators, [Freezed](https://pub.dartlang.org/packages/freezed) will need you to both import the annotation ([freezed\_annotation](https://pub.dartlang.org/packages/freezed_annotation)) and use the `part` keyword on the top of your files.
As such, a file that wants to use [Freezed](https://pub.dartlang.org/packages/freezed) will start with:
```
import 'package:freezed_annotation/freezed_annotation.dart';
part 'my_file.freezed.dart';
```
copied to clipboard
**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](https://pub.dartlang.org/packages/freezed) will automatically do it for you.
Freezed offers two ways of creating data-classes:
- [Primary constructors](https://pub.dev/packages/freezed#primary-constructors) ; where you define a constructor and Freezed generates the associated fields. This is simulating the [Primary Constructor](https://github.com/dart-lang/language/issues/2364) using `factory`.
- [Classic classes](https://pub.dev/packages/freezed#classic-classes), where you write a normal Dart class and Freezed only handles `toString/==/copyWith`
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);
}
```
copied to clipboard
The following snippet defines a model named `Person`:
- `Person` has 3 properties: `firstName`, `lastName` and `age`
- 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 a `toJson` method for us.
- Freezed will also automatically generate:
- a `copyWith` method, for cloning the object with different properties
- a `toString` override listing all the properties of the object
- an `operator ==` and `hashCode` override (since `Person` is immutable)
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 `factory` keyword as showcased (`const` is 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');
}
}
```
copied to clipboard
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');
}
}
```
copied to clipboard
#### 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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
#### 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;
}
```
copied to clipboard
**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](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
This syntax gives full control over inheritance.
Of course, you can also opt-out of `factory` constructors and write normal classes. See [Classic Classes](https://pub.dev/packages/freezed#classic-classes).
In general, this workaround makes more sense for [Unions](https://pub.dev/packages/freezed#union-types), 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);
}
```
copied to clipboard
This defines a model mostly identical to our previous snippets, but with the following differences:
- `firstName` and `lastName` are now mutable. As such, we can write:
```
void main() {
var person = Person(firstName: 'John', lastName: 'Smith', age: 42);
person.firstName = 'Mona';
person.lastName = 'Lisa';
}
```
copied to clipboard
- `age` is still immutable, because we explicitly marked the property as `final`.
- `Person` no-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
}
```
copied to clipboard
- Of course, since our `Person` class is mutable, it is no-longer possible to instantiate it using `const`.
#### 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
}
```
copied to clipboard
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
}
```
copied to clipboard
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);
}
```
copied to clipboard
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.
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;
}
```
copied to clipboard
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)
}
```
copied to clipboard
Notice Freezed supports `person.copyWith(age: null)`.
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;
}
```
copied to clipboard
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',
),
),
);
```
copied to clipboard
This *works*, but is relatively verbose with a lot of duplicates.
This is where we could use [Freezed](https://pub.dartlang.org/packages/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');
```
copied to clipboard
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');
```
copied to clipboard
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(...));
```
copied to clipboard
**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');
```
copied to clipboard
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');
```
copied to clipboard
[Freezed](https://pub.dartlang.org/packages/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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
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;
}
```
copied to clipboard
This will deprecate both:
- The constructor
```
Person(gender: Gender.something); // gender is deprecated
```
copied to clipboard
- The generated class's constructor:
```
_Person(gender: Gender.something); // gender is deprecated
```
copied to clipboard
- the property:
```
Person person;
print(person.gender); // gender is deprecated
```
copied to clipboard
- the `copyWith` parameter:
```
Person person;
person.copyWith(gender: Gender.something); // gender is deprecated
```
copied to clipboard
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;
}
```
copied to clipboard
While [Freezed](https://pub.dartlang.org/packages/freezed) will not generate your typical `fromJson`/`toJson` by itself, it knows what [json\_serializable](https://pub.dev/packages/json_serializable) is.
Making a class compatible with [json\_serializable](https://pub.dev/packages/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;
}
```
copied to clipboard
The changes necessary to make it compatible with [json\_serializable](https://pub.dev/packages/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);
}
```
copied to clipboard
Don't forget to add `json_serializable` to your `pubspec.yaml` file:
```
dev_dependencies:
json_serializable:
```
copied to clipboard
That's it\!
With these changes, [Freezed](https://pub.dartlang.org/packages/freezed) will automatically ask [json\_serializable](https://pub.dev/packages/json_serializable) to generate all the necessary `fromJson`/`toJson`.
**Note**:
Freezed will only generate a fromJson if the factory is using `=>`.
For classes with multiple constructors, [Freezed](https://pub.dartlang.org/packages/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);
}
```
copied to clipboard
Then [Freezed](https://pub.dartlang.org/packages/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()"
}
]
```
copied to clipboard
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);
}
```
copied to clipboard
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()"
}
]
```
copied to clipboard
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
```
copied to clipboard
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();
}
```
copied to clipboard
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);
}
```
copied to clipboard
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);
}
```
copied to clipboard
**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](https://github.com/google/json_serializable.dart/tree/master/json_serializable#build-configuration)).
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);
}
```
copied to clipboard
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
```
copied to clipboard
**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);
}
```
copied to clipboard
**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);
}
```
copied to clipboard
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](https://pub.dev/packages/json_serializable#build-configuration).
See also the [decorators](https://pub.dev/packages/freezed#decorators-and-comments) section
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](https://pub.dartlang.org/packages/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;
}
```
copied to clipboard
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
}
```
copied to clipboard
We'll see why in the following section.
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;
}
```
copied to clipboard
Then you will be unable to read `age` and `population` directly:
```
var example = Example.person('Remi', 24);
print(example.age); // does not compile!
```
copied to clipboard
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
```
copied to clipboard
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)
```
copied to clipboard
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
```
copied to clipboard
To solve this problem, we need check the state of our object using what we call "pattern matching".
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;
}
```
copied to clipboard
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)'),
}
```
copied to clipboard
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)');
}
```
copied to clipboard
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 [\#](https://pub.dev/packages/freezed#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;
}
```
copied to clipboard
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>;
}
```
copied to clipboard
**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](https://pub.dev/packages/freezed#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.
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;
}
```
copied to clipboard
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>
}
```
copied to clipboard
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._();
}
```
copied to clipboard
#### (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`/`map` are kept for users who have yet to migrate to Dart 3. But in the long term, you should stop relying on them and migrate to `switch` expressions.
##### 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;
}
```
copied to clipboard
Then \[when\] will be:
```
var union = Union(42);
print(
union.when(
(int value) => 'Data $value',
loading: () => 'loading',
error: (String? message) => 'Error: $message',
),
); // Data 42
```
copied to clipboard
Whereas if we defined:
```
@freezed
sealed class Model with _$Model {
factory Model.first(String a) = First;
factory Model.second(int b, bool c) = Second;
}
```
copied to clipboard
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
```
copied to clipboard
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;
}
```
copied to clipboard
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
```
copied to clipboard
\[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
```
copied to clipboard
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)
```
copied to clipboard
Freezed offers various options to customize the generated code. To do so, there are two possibilities:
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;
}
```
copied to clipboard
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 {...}
```
copied to clipboard
To view all the possibilities, see the documentation of `@Freezed`: <https://pub.dev/documentation/freezed_annotation/latest/freezed_annotation/Freezed-class.html>
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/
```
copied to clipboard
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
```
copied to clipboard
The [Freezed](https://marketplace.visualstudio.com/items?itemName=blaxou.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 [\#](https://pub.dev/packages/freezed#freezed-extension-for-intellijandroid-studio)
You can get Live Templates for boiler plate code [here](https://github.com/Tinhorn/freezed_intellij_live_templates).
Example:
- type **freezedClass** and press `Tab` to generate a freezed class
```
@freezed
class Demo with _$Demo {
}
```
copied to clipboard
- type **freezedFromJson** and press `Tab` to generate the fromJson method for json\_serializable
```
factory Demo.fromJson(Map<String, dynamic> json) => _$DemoFromJson(json);
```
copied to clipboard
You can add `freezed` specific linting rules that provide helpful utilities and catch common mistakes when creating `freezed` classes.
Add [`custom_lint`](https://pub.dev/packages/custom_lint) and `freezed_lint` to your `pubspec.yaml`:
```
dart pub add dev:custom_lint
dart pub add dev:freezed_lint
```
copied to clipboard
Also add `custom_lint` to your `analysis_options.yaml`:
```
analyzer:
plugins:
- custom_lint
```
copied to clipboard
This part contains community-made tools which integrate with Freezed.
[DartJ](https://dartj.web.app/#/) is Flutter application, made by [**@ttpho**](https://github.com/ttpho), which will generate the Freezed classes from a JSON payload.
Example:
<https://github.com/ttpho/ttpho/assets/3994863/5d529258-c02c-4066-925e-ca2ffc68a804>
[](https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg) |
| Shard | 66 (laksa) |
| Root Hash | 2116194247637282866 |
| Unparsed URL | dev,pub!/packages/freezed s443 |