
불변 데이터 클래스(Immutable Data Class)를 쉽게 만들어주는 패키지다.
풀어서 설명하면 Dart에서 immutable, copyWith, ==, hashCode, toString, union/sealed class 등을 자동 생성해주는 코드 생성 패키지
문득 생각이 든게 DB데이터 변환하고 하다보면 킹받는데 얘로 다 끝낼 수 있을 것 같다.
그래서 봤지만 한계는 명확하다고 한다.

@freezed
class Car with _$Car {
const factory Car({
required String model,
required int year,
required Engine engine,
}) = _Car;
factory Car.fromJson(Map<String, dynamic> json) => _$CarFromJson(json);
}
final car = Car.fromJson(jsonMap);
final updated = car.copyWith(year: 2025);
print(updated); // 디버깅도 깔끔
대충 위처럼 편하게 데이터 처리 가능.
나는 riverpod이 좋다 flutter하면서 제일 처음 배웠기 때문. 첫사랑같은 느낌.

그래서 freezed + riverpod 조합에 대해 파보려고함.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_model.freezed.dart';
part 'user_model.g.dart';
@freezed
class User with _$User {
const factory User({
required String name,
required int age,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
모델을 위처럼 freezed로 예쁘게 만들어주고,
import 'package:freezed_annotation/freezed_annotation.dart';
import 'user_model.dart';
part 'user_state.freezed.dart';
@freezed
class UserState with _$UserState {
const factory UserState.initial() = _Initial;
const factory UserState.loading() = _Loading;
const factory UserState.data(User user) = _Data;
const factory UserState.error(String message) = _Error;
}
상태 클래스도 만들어준다.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'user_state.dart';
import 'user_model.dart';
class UserNotifier extends StateNotifier<UserState> {
UserNotifier() : super(const UserState.initial());
Future<void> fetchUser() async {
state = const UserState.loading();
try {
// 실제 API 호출이 있다고 가정
await Future.delayed(const Duration(seconds: 2));
final user = User(name: '장원', age: 30);
state = UserState.data(user);
} catch (e) {
state = UserState.error('사용자 정보를 불러오지 못했습니다');
}
}
}
final userProvider = StateNotifierProvider<UserNotifier, UserState>((ref) {
return UserNotifier();
});
상태 Notifier도 만들어준다.
final userState = ref.watch(userProvider);
userState.when(
initial: () => const Text("초기 상태"),
loading: () => const CircularProgressIndicator(),
data: (user) => Text("안녕하세요 ${user.name}님"),
error: (msg) => Text("에러: $msg"),
);
그럼 화면에서 위와 같이 사용할 수 있다.
화면은 Notifier에 의해 변경되고, 미리 정의한 상태와 모델을 통해 데이터가 깔끔해짐.

하지만 위의 예제로는 Notifier를 쓸 필요는 없다
유저와의 상호작용이 없는 1회용 호출이기 때문이다.
이럴 때는 그냥 FutureProvider를 통해 불러오면 깔끔하다고함.
FutureProvider + Notifier의 장점만 결합한 최신 상태 관리 방식이라고함.
대충 봤는데, 너무 복잡하다.
편하게 만들어줘 누가 ㅠㅠ
난 못해..
@Freezed(
toJson: true, // toJson 자동 생성
fromJson: true, // fromJson 자동 생성
makeCollectionsUnmodifiable: true, // List, Map을 unmodifiable하게
equal: true, // == 및 hashCode 자동 생성 (기본 true)
)
const factory User({
@Default('이름없음') String name,
@Default(0) int age,
}) = _User;
쓰다보니 엄청 당연한 것만 있는데.. 이걸 안쓰면 얼마나 개차반으로 써야하는거지?
freezed가 너무 당연하게 느껴질 정도로 편한 이유는, 이전 방식이 고통 그 자체였기 때문이라고한다.

대충 다 만들었어야 한다고 한다.
class User {
final String name;
final int age;
const User({required this.name, required this.age});
User copyWith({String? name, int? age}) {
return User(
name: name ?? this.name,
age: age ?? this.age,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age;
@override
int get hashCode => name.hashCode ^ age.hashCode;
@override
String toString() => 'User(name: $name, age: $age)';
}
막 더럽진 않은데..?
중복 코드도 심하고, 실수할 여지가 있어서 freezed로 생성하는게 맘 편하다고함.
Remi Rousselet이라는 사람이라고 한다.

이력을 찾아봤다.
Invertase 소속의 프로그래머고, 프랑스 파리에 산다고 한다.
42(프랑스의 코딩스쿨)를 졸업했고, 7년 이상의 경력이 있다고 한다.
주요 기여는 freezed, riverpod, provider, flutter_hooks 등을 개발 및 유지하고 있다고한다.
Deeply in love with Flutter 라고 말할 정도로 플러터를 사랑한다고함.
작고 강력한 코드 모델을 만드는 것이 큰 의미가 있다를 반복해서 쓴다고 한다.
이 사람이 riverpod에서는 도대체 어떤걸 한건지 궁금해짐.
Riverpod 저장소의 많은 기능 구조(Provider composition, scoping, Future/Stream 지원 등)는 Remi가 직접 설계하고 구현했다고 함.
유튜브도 나오고
flutter 공식 채널에도 나오셨군..
이런 개발자가 되도록 열심히 해야겠다..