freezed 是 Flutter 开发中用于生成不可变数据类的最佳工具,它通过注解和代码生成,显著减少样板代码并提升代码健壮性。
为什么 freezed 成为 Flutter 数据类的首选?
什么是 freezed?
freezed 是一个基于 Dart 注解的代码生成库,专门为 Flutter 项目提供不可变数据类,它自动生成 copyWith、toString、hashCode、 等常用方法,同时支持联合类型(sealed union)和可空类型,业内专家指出,freezed 的设计理念受 Kotlin 的 data class 启发,但在 Dart 中通过代码生成实现了更灵活的模式。
使用 freezed 的核心优势
- 减少样板代码:手动编写数据类需要写大量重复方法,freezed 自动生成,节省约 50% 的代码量(根据社区经验)。
- 不可变保证:所有字段默认
final,配合copyWith实现安全更新,避免状态突变引发的 bug。 - 联合类型支持:可使用
@freezed配合sealed class轻松处理多态场景,如网络请求结果(成功/失败)。 - 序列化集成:与
json_serializable无缝配合,一行注解即可完成 JSON 序列化。 - 测试友好:生成的方法覆盖 和
hashCode,使测试中的对象比较更可靠。
freezed 怎么用?从安装到生成代码的详细教程
环境配置与依赖添加
在 pubspec.yaml 中添加依赖:
dependencies: freezed_annotation: ^2.4.0 json_annotation: ^4.8.0 dev_dependencies: freezed: ^2.4.0 json_serializable: ^6.7.0 build_runner: ^2.4.0
运行 flutter pub get 完成安装。
创建第一个 freezed 数据类
定义一个用户模型:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
@freezed
class User with _$User {
const factory User({
required String name,
@Default(0) int age,
String? email,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
代码生成与运行
终端执行:
flutter pub run build_runner build --delete-conflicting-outputs
生成 user.freezed.dart 和 user.g.dart,之后即可使用生成的 copyWith、toJson 等方法。
使用示例
final user = User(name: 'John', age: 25); final updatedUser = user.copyWith(age: 26); print(user == updatedUser); // false
freezed 与 json_serializable 对比:哪个更适合你的 Flutter 项目?
很多开发者纠结于 freezed 和纯 json_serializable 的选择,以下对比能帮你快速决策:
| 对比维度 | freezed | json_serializable |
|---|---|---|
| 代码生成 | 自动生成数据类、copyWith、==、hashCode 等 | 仅生成 fromJson/toJson |
| 不可变性 | 强制执行,所有字段可设为 final | 需手动声明 final |
| 联合类型 | 原生支持 | 需额外处理 |
| 配置复杂度 | 中等(需 freezed_annotation) | 低(只需 json_annotation) |
| 适用场景 | 状态管理、复杂业务逻辑 | 简单数据传输、API 响应序列化 |
如果你的项目涉及状态管理(如 Bloc、Riverpod)或需要保障数据不可变,freezed 是更优选择;如果只是单纯做 JSON 序列化,纯 json_serializable 更轻量。
freezed 在 Flutter 项目中的实际应用场景
状态管理中的不可变数据
在 Bloc 或 Riverpod 中,状态必须不可变以触发正确的重建:
@freezed
class CounterState with _$CounterState {
const factory CounterState({
@Default(0) int count,
}) = _CounterState;
}
使用 copyWith 更新状态,确保每次都是新对象,避免引用不变导致的 UI 不更新问题。
处理 API 响应
结合 sealed class 优雅处理网络请求结果:
@freezed
class ApiResult<T> with _$ApiResult<T> {
const factory ApiResult.success(T data) = _Success;
const factory ApiResult.error(String message) = _Error;
}
在视图中通过 when 或 maybeWhen 处理不同分支,无需 if-else 判断。
联合类型与枚举
freezed 的联合类型模式比传统枚举更灵活,可携带不同字段:
@freezed
class Payment with _$Payment {
const factory Payment.creditCard({required String number}) = _CreditCard;
const factory Payment.wechat({required String openId}) = _WeChat;
const factory Payment.alipay({required String account}) = _Alipay;
}
这样每个支付类型自带不同属性,更方便扩展。
freezed 常见问题与解决方案
代码生成失败怎么办?
- 检查依赖版本:确保 freezed 和 freezed_annotation 版本匹配。
- 删除冲突输出:运行
flutter pub run build_runner clean再重新生成。 - 更新 build_runner
:使用
flutter pub upgrade确保最新版本。
如何与 json_serializable 同时使用?
只需在类上同时添加 @freezed 和 @JsonSerializable,并编写 fromJson 工厂方法:
@freezed
class User with _$User {
const factory User({
required String name,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
生成器会自动处理序列化。
为什么 copyWith 生成的字段为可选?
freezed 生成的 copyWith 方法参数全部为可选,这是为了只更新需要修改的字段,保持不可变。
user.copyWith(age: 30); // 只更新 age,其他字段不变
如果字段类型为 int?,copyWith 可以使用 null 表示清空,需要显式传递 null。
Q&A:freezed 相关问题
freezed 在 Flutter 中怎么用?
安装依赖后,在数据类上添加 @freezed 注解,运行 build_runner 生成代码,即可使用自动生成的 copyWith、、toString 等方法,适用于状态管理、API 模型等场景。
freezed 和 equatable 有什么区别?
equatable 仅提供 和 hashCode 重写,需要手动实现所有字段的相等比较,freezed 除此外还生成 copyWith、toString、联合类型支持,并强制不可变性,功能更全面,但需额外配置代码生成。
freezed Flutter 项目中的应用广吗?
Flutter 社区中,freezed 被广泛用于状态管理和数据模型定义,尤其是 Bloc 和 Riverpod 用户群体,因其能显著提升代码质量和开发效率,已成为不少中大型项目的标配。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/511637.html



