The flutter is a UI tool cut that Google has developed. It has gained immense popularity from the same code base for the ability to create beautiful and locally compiled applications for mobile, web, and desktop.
While the dart, the language behind the flurry, is powerful, writing the data models often involve repeated and mistaken tasks. A normal model may be required:
A converter and a description of features
Over -riding
toStringFor, for, for,.==Operator, andhashCodeImplement A.
copyWithMethodWriting serialization (
toJson) And dyserialization (fromJson) Ways
Doing all this by hand can swift your code faster and reduce the ability to read.
This is the place where frozen. Frozen is a Dart Code generator that develops a boiler plate for unintentional data classes, unions, patterns, cloning, and JSON serialization. With frozen, you can write a comprehensive and secure model while the package handles repeated parts.
In this tutorial, you will learn how to use freezing data classes, creating JSON serialization, and securing a variety of powerful unions to handle multiple states. Finally, you will find a way to reduce the boiler plate and make it easier to clean and maintain your blowing code.
The table of content
Provisions
Before starting, you should be comfortable:
Fluttering: Be able to create a new fluttering project and run it on an emulator or device.
The basic principles of the dart language: Understand how classes, constructors and methods work.
Command line tools: Like being able to run a command
flutter pub getOrflutter pub run.Json concepts: Know what Json is and how it is commonly used for API data exchange.
If you are already pleased with these titles, you are ready to sink in frozen.
Why frozen?
When fluttering, two challenges arise when working with the data model: Instability And Serialization. Frozen helps to solve both clearly and automatically.
1. Istability
In the dart, the items are variable as default. This means that once you make an item, its fields can be changed anywhere in your code. Although simple, it can produce neutral side effects, such as mistakenly editing the user object in a part of your app and breaking the logic elsewhere.
Manually requires a lot of boiler plate to ensure instability: you must announce all fields finalEnforce copyWith Ways to make edited copies, and correctly override == And hashCode To maintain the equation of the object. It can be repeated and mistaken.
How does frozen help:
Frozen automatically produces irreversible classes. All fields are finalAnd a copyWith The method has been provided so that you can safely create edited copies without changing the original item. Pls, frozen handles == And hashCode Your thing, which makes sure that your items are used properly when you are used in comparison or collecting. This reduces the boiler plate rapidly, implementing instability.
2. Serialization
When interacting with APIS, changing dart items in JSON and it is a common task. Without automation, you have to write toJson And fromJson Ways to each class, carefully map each field. It is easy to repeatedly and wrong, especially when your models change over time.
How does frozen help:
Is integrated with frozen json_serializable Package to automatically create serialization and deserralization logic. You just interpret your class and run the code generator, and then the frozen is created fully from working toJson And fromJson Ways for you. This not only saves time but also reduces the chances of mistakes and keeps your code clean and maintained.
Without frozen: a manual instance
What is a basic here User Class looks like frozen:
class User {
final String name;
final int age;
final String email;
const User({
required this.name,
required this.age,
required this.email,
});
User copyWith({
String? name,
int? age,
String? email,
}) {
return User(
name: name ?? this.name,
age: age ?? this.age,
email: email ?? this.email,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
'email': email,
};
}
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json('name') as String,
age: json('age') as int,
email: json('email') as String,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age &&
email == other.email;
@override
int get hashCode => name.hashCode ^ age.hashCode ^ email.hashCode;
@override
String toString() {
return 'User{name: $name, age: $age, email: $email}';
}
}
This is verb, and it is easy to lose details like refreshing hashCode When adding new fields.
With frozen: a clean replacement
Now when you consider these challenges a frozen solution, let’s see how it makes it easy and clean to work with the data model. In this section, you will install the necessary packages, set up a frozen class, and create a boiler plate code. Once this setup is completed, we will dive into examples in which it shows how to use the frozen class, including items and JSON serialization.
First, install frozen and related packages. Add it to your own Pub Spring.mal File:
dependencies:
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
dev_dependencies:
flutter_lints: ^2.0.0
build_runner: ^2.0.0
freezed: ^2.4.7
json_serializable: ^6.7.1
Then run away:
flutter pub get
For pure dart projects, use:
dart pub get
Frozen class descriptions
Create a file that has a name user.dart And add the following:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
@freezed
class User with _$User {
factory User({required String name, required int age}) = _User;
}
What is happening in this code is here:
import 'package:freezed_annotation/freezed_annotation.dart';: Desired interpretations by frozen.part 'user.freezed.dart';: It indicates that the frozen will produce the code in this file.@freezed: The following class tells to be frozen to take action.class User with _$User: DeclaresUserClasswith _$UserPart connects the class with a code created.factory User({required String name, required int age}) = _User;: A factory conductor explains. Being frozen creates processing classes (_User) Behind the scenes.
Code Generation is running
Run the following command to prepare the code:
flutter pub run build_runner watch --delete-conflicting-outputs
For Dart Plans:
dart pub run build_runner watch --delete-conflicting-outputs
It creates user.freezed.dart File, which is like a boiler plate copyWithFor, for, for,. ==For, for, for,. hashCodeAnd toString.
Using a frozen class
Let’s see the frozen in action:
void main() {
final user = User(name: 'John Doe', age: 25);
final user2 = user.copyWith(name: 'Jane Doe');
final user3 = user2;
print(user);
print(user2);
print(user2 == user3);
print('Name: ${user.name}');
print('Age: ${user.age}');
}
What is happening here:
final user = User(name: 'John Doe', age: 25);: Creates a new unmannedUser.final user2 = user.copyWith(name: 'Jane Doe');: Creates a copy ofuserWith a new name but maintains the same age.final user3 = user2;: Pointsuser3As a single itemuser2.print(user);: Thanks to the born, shows the wire of readingtoString.print(user2 == user3);: Compares the objects produced==.
Adding json serialization
Refusal user.dart To support JSON:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
@freezed
class User with _$User {
factory User({required String name, required int age}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
In the new parts of the code:
Next, run the generator once again:
flutter pub run build_runner build --delete-conflicting-outputs
Using JSON serialization
Examples use:
void main() {
final userJson = {'name': 'Alice', 'age': 30};
final user = User.fromJson(userJson);
print('Name: ${user.name}');
print('Age: ${user.age}');
final userBackToJson = user.toJson();
print('Back to JSON: $userBackToJson');
}
In this code:
Advanced use: Frozen unions
So far, we have used frozen for unintentional data models. Is another powerful feature of frozen Unions (Also known as sealed classes).
Unions allow you to represent multiple potential states of an item in a type of safe way. This is especially useful in the flurry when it works with unprecedented tasks such as API calls, where you often have states. loadingFor, for, for,. successAnd error.
Union’s explanation
Create a new file that says result.dart:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'result.freezed.dart';
@freezed
class Result<T> with _$Result<T> {
const factory Result.loading() = Loading;
const factory Result.success(T data) = Success;
const factory Result.error(String message) = Error;
}
Line Byline Code Specification:
import 'package:freezed_annotation/freezed_annotation.dart';: Imports the interpretation library required to freeze.part 'result.freezed.dart';: This file tells to freeze the boiler plate to produce plate.@freezedFrozen guidance to prepare a code for the interpretation class.class Result: Announces normal classwith _$Result ResultWhich type of data can be keptT.const factory Result.loading() = Loading: Describes; loadingStateLoadingIs the class born.const factory Result.success(T data) = Success: Describes; successState with relevant data.const factory Result.error(String message) = Error: Describes; errorState with a message.
After savings, prepare the code:
flutter pub run build_runner build --delete-conflicting-outputs
Using the union
Let’s use our use of an API call and return results Result Union:
FutureString>> fetchUserData() async {
await Future.delayed(const Duration(seconds: 2));
final success = true;
if (success) {
return const Result.success("User data fetched successfully");
} else {
return const Result.error("Failed to fetch user data");
}
}
What is happening here:
Future: Loots A> fetchUserData() ResultThe thing that containsStringDataawait Future.delayed(...): A real network call calls the call, a delay of 2 seconds.if (success) { ... } else { ... }: Rule either AsuccessOrerrorConclusion
The pattern matching with frozen
A great part of the frozen .He is Pattern matching. You can handle all the states without long writing if Check
void main() async {
final result = await fetchUserData();
result.when(
loading: () => print("Loading..."),
success: (data) => print("Success: $data"),
error: (message) => print("Error: $message"),
);
}
What is happening in this code is here:
This ensures that all states have been handled. If you forget someone, the composition will show a mistake.
Mibon: Deliver from partial states
maybeWhen Have a secure and more flexible version when. While when You need to handle All possible statesFor, for, for,. maybeWhen Just lets you handle the people you care about and gives the Fallback with it orElse.
When you are not interested in every state, but only one sub -set.
Sometimes you just care about some states. This is how you can use maybeWhen:
result.maybeWhen(
success: (data) => print("Data received: $data"),
orElse: () => print("No data"),
);
What is happening here:
success: (data)Only run when the current state issuccess.orElseWorks as a flockback for all other states (loadingFor, for, for,.errorEtc.).
So in this piece, the code is showing how you can only react to the state of success while safely ignoring everything.
Map: Working directly with state items
Have another point of view mapWhich provides a full -class example:
result.map(
loading: (value) => print("Currently loading"),
success: (value) => print("Got success: ${value.data}"),
error: (value) => print("Got error: ${value.message}"),
);
Here, each branch receives the created class (LoadingFor, for, for,. SuccessFor, for, for,. Error), Providing you with access to all fields.
Why use unions?
Unions shine when they make apps with irreversible logic. For example:
Network requests:
loadingFor, for, for,.successFor, for, for,.errorVerification of verification:
validFor, for, for,.invalidFor, for, for,.submittingConfirmation:
authenticatedFor, for, for,.unauthenticatedFor, for, for,.loading
Instead of writing bool isLoading And String? error Flags scattered in your app, unions provide you with a systematic, type -secure way for the model state.
Conclusion
Frozen filter is an essential tool for developers that want to reduce the boiler plate while maintaining a safe, undeniable and easily serializable model.
By handling the repeated code copyWithChecking equality, and JSon serialization, frozen, allows you to focus on building applications instead of writing a boiler plate.
Whether you are early or experienced developer, frozen and improve your code base reading, safety and maintaining.
For advanced features and best methods, go to official frozen documents Pub.dev.