Flutter Freezed | Models, copyWith, JSON Serialization, Generics & Union Types

Make sure to watch json_seriliazble first.

Freezed class

address.dart
{
  "success": true,
  "data": {
    "street": "123 Main Street",
    "district": "Dhaka",
    "country": "Bangladesh"
  },
  "message": "Address fetched"
}
import 'package:freezed_annotation/freezed_annotation.dart';

part 'address.freezed.dart';
part 'address.g.dart';

@freezed
abstract class Address with _$Address {
  const factory Address({
    required String street,
    required String district,
    required String country,
  }) = _Address;

  factory Address.fromJson(Map<String, Object?> json) =>
      _$AddressFromJson(json);

  static const Address present = Address(
    street: "present",
    district: "addr.dis",
    country: "BD",
  );

  static const Address permanent = Address(
    street: "permanent",
    district: "addr.dis",
    country: "...",
  );
}
person.dart
{
  "success": true,
  "data": {
    "name": "John Doe",
    "age": 25,
    "address": "Earth",
    "skills": ["Dart", "Flutter"],
    "presentAddress": {
      "street": "123 Main Street",
      "district": "Dhaka",
      "country": "Bangladesh"
    },
    "permanentAddress": {
      "street": "45 Lake Road",
      "district": "Chattogram",
      "country": "Bangladesh"
    }
  },
  "message": "Person fetched"
}
import 'package:freezed_annotation/freezed_annotation.dart';
import 'address.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
abstract class Person with _$Person {
  const factory Person({
    required String name,
    int? age,
    @Default("Eath") String? address,
    @Default([]) List<String> skills,

    required Address presentAddress,
    Address? permanentAddress,
  }) = _Person;

  factory Person.fromJson(Map<String, Object?> json) => _$PersonFromJson(json);

  static const Person ui = Person(
    name: "0",
    age: 0,
    skills: [],
    presentAddress: Address.present,
    permanentAddress: Address.permanent,
  );
}

Generic

api_response.dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'api_response.freezed.dart';
part 'api_response.g.dart';

@Freezed(genericArgumentFactories: true)
abstract class ApiResponse<T> with _$ApiResponse<T> {
  const factory ApiResponse({
    required bool success,
    T? data,
    String? message,
  }) = _ApiResponse<T>;

  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
  ) => _$ApiResponseFromJson(json, fromJsonT);
}

Union

code-snippet

We are using type instead of runtimetype

code

import 'package:freezed_annotation/freezed_annotation.dart';

part 'response.freezed.dart';
part 'response.g.dart';

@Freezed(genericArgumentFactories: true, unionKey: "type")
sealed class Response<T> with _$Response<T> {
  const factory Response.data(T value) = ResponseData<T>;
  const factory Response.error(Object error) = ResponseError<T>;

  factory Response.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
  ) => _$ResponseFromJson(json, fromJsonT);
}

sample json

final jsonUnionPerson = {
  "type": "error",
  "value": {
    "name": "John Doe",
    "age": 25,
    "address": "Earth",
    "skills": ["Dart", "Flutter"],
    "presentAddress": {
      "street": "123 Main Street",
      "district": "Dhaka",
      "country": "Bangladesh",
    },
    "permanentAddress": {
      "street": "45 Lake Road",
      "district": "Chattogram",
      "country": "Bangladesh",
    },
  },
};

final jsonUnionAddress = {
  "type": "error",
  "value": {
    "street": "123 Main Street",
    "district": "Dhaka",
    "country": "Bangladesh",
  },
};

final personErrorJson = {
  "type": "error",
  "error": "Person not found",
};

freezed on pub.dev↗