Using Freezed in Flutter: Data Classes, Unions, and Code Simplified

Flutter Freezed

Writing clean, immutable data classes in Dart can get repetitive fast. That’s where Freezed comes in—a powerful code generator that simplifies model creation, enables union/sealed classes, and eliminates boilerplate. In this guide, you’ll learn how to use Freezed to build better data models and manage state more effectively in your Flutter apps.

What Is Freezed?

Freezed is a Dart package that lets you create immutable classes, copy methods, deep equality, pattern matching, and sealed classes with almost no boilerplate.

It’s often used with state management tools like Riverpod, BLoC, and Cubit to manage model and state classes efficiently.

Why Use Freezed?

  • Eliminates repetitive code (e.g., ==, hashCode, copyWith)
  • Enables union types (a.k.a. sealed classes)
  • Makes your models testable and predictable
  • Fully integrates with Dart’s null safety
  • Works great with JSON serialization

Step 1: Install Freezed and Dependencies

Add the following to your pubspec.yaml:

dependencies:
  freezed_annotation: ^2.4.1

dev_dependencies:
  build_runner: ^2.4.6
  freezed: ^2.4.5

Then run:

flutter pub get

Step 2: Create a Basic Data Class with Freezed

Create a file called user_model.dart:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user_model.freezed.dart';

@freezed
class UserModel with _$UserModel {
  const factory UserModel({
    required String id,
    required String name,
    int? age,
  }) = _UserModel;
}

Then run the generator:

flutter pub run build_runner build --delete-conflicting-outputs

Now you have an immutable class with:

  • Deep equality
  • copyWith
  • Readable toString()

Step 3: Use copyWith in Practice

final user = UserModel(id: '1', name: 'John');
final updatedUser = user.copyWith(name: 'Jane');

This creates a new instance with the updated value, without modifying the original—keeping your data flow safe and predictable.

Step 4: Create Union Types (Sealed Classes)

You can also use Freezed to create unions, perfect for representing different states.

Example: AuthState

@freezed
class AuthState with _$AuthState {
  const factory AuthState.unauthenticated() = Unauthenticated;
  const factory AuthState.authenticated(String userId) = Authenticated;
  const factory AuthState.loading() = Loading;
}

Use it like this:

void handleState(AuthState state) {
  state.when(
    unauthenticated: () => print('Not logged in'),
    authenticated: (id) => print('Logged in as $id'),
    loading: () => print('Loading...'),
  );
}

Bonus: Use maybeWhen and map

Freezed gives you more advanced control for optional or partial matches:

state.maybeWhen(
  authenticated: (id) => print('Hi $id'),
  orElse: () => print('Default state'),
);

This keeps your logic super readable and safe.

When to Use Freezed

Use Freezed when:

  • You want clean, immutable models
  • You’re tired of writing ==, hashCode, and copyWith manually
  • You’re building complex state machines
  • You’re working with APIs and need JSON serialization

Freezed supports integration with json_serializable as well.

Conclusion

Freezed is one of the best tools in a Flutter developer’s toolkit. It removes repetitive code, introduces sealed classes, and makes your models easier to read, write, and maintain. Whether you’re working on a simple app or a large production project, using Freezed will keep your code clean and consistent.

Start small—build a model with Freezed today, and you’ll never want to go back.

Leave a Comment

Your email address will not be published. Required fields are marked *

1 thought on “Using Freezed in Flutter: Data Classes, Unions, and Code Simplified”

Scroll to Top