如何使用Flutter Freezed包在模型中生成DateTime类型字段
正确生成含DateTime字段的Freezed模型类(适配Firestore Timestamp)
问题场景
编写的Freezed模型代码如下:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; @freezed class UserModel with _$UserModel { factory UserModel({ required String id, @Default('') String uniqueId, @Default(DateTime.now()) DateTime dob, }) = _UserModel; factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json); }
使用DateTime.now()作为dob字段默认值时,无法生成所需的.g.dart文件。将代码修改为把dob设为必填字段后,虽然能正常生成文件,但生成的fromJson代码会将dob按字符串解析,需要手动修改为适配Firestore Timestamp的逻辑:
// 生成的原始代码 dob: DateTime.parse(json['dob'] as String), // 需要手动改成 dob: (json['dob'] as Timestamp?)?.toDate() ?? DateTime.now()
不想每次生成代码后手动修改,需要找到正确的配置方式。
解决方案
核心思路是通过自定义Json转换器让Freezed自动处理Firestore Timestamp与Dart DateTime的转换,同时通过合理的方式设置运行时默认值。
步骤1:实现Timestamp与DateTime的转换器
创建两个转换器(分别处理非空和可空的DateTime字段):
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; // 处理非空DateTime与Timestamp的转换 class TimestampConverter implements JsonConverter<DateTime, Timestamp> { const TimestampConverter(); @override DateTime fromJson(Timestamp timestamp) => timestamp.toDate(); @override Timestamp toJson(DateTime date) => Timestamp.fromDate(date); } // 处理可空DateTime与Timestamp的转换 class NullableTimestampConverter implements JsonConverter<DateTime?, Timestamp?> { const NullableTimestampConverter(); @override DateTime? fromJson(Timestamp? timestamp) => timestamp?.toDate(); @override Timestamp? toJson(DateTime? date) => date != null ? Timestamp.fromDate(date) : null; }
步骤2:在模型中应用转换器并处理默认值
由于DateTime.now()是运行时计算的值,不能直接作为@Default的参数(Freezed要求默认值为编译时常量),可以选择以下两种写法:
写法一:非空字段+常量默认值+工厂函数替换
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; class TimestampConverter implements JsonConverter<DateTime, Timestamp> { const TimestampConverter(); @override DateTime fromJson(Timestamp timestamp) => timestamp.toDate(); @override Timestamp toJson(DateTime date) => Timestamp.fromDate(date); } @freezed class UserModel with _$UserModel { factory UserModel({ required String id, @Default('') String uniqueId, // 用编译时常量作为默认值,同时应用转换器 @TimestampConverter() @Default(DateTime(0)) DateTime dob, }) = _UserModel; // 自定义fromJson,将默认的DateTime(0)替换为当前时间 factory UserModel.fromJson(Map<String, dynamic> json) { final rawModel = _$UserModelFromJson(json); return rawModel.copyWith( dob: rawModel.dob == DateTime(0) ? DateTime.now() : rawModel.dob, ); } }
写法二:可空字段+工厂函数补全默认值
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; class NullableTimestampConverter implements JsonConverter<DateTime?, Timestamp?> { const NullableTimestampConverter(); @override DateTime? fromJson(Timestamp? timestamp) => timestamp?.toDate(); @override Timestamp? toJson(DateTime? date) => date != null ? Timestamp.fromDate(date) : null; } @freezed class UserModel with _$UserModel { factory UserModel({ required String id, @Default('') String uniqueId, // 可空字段+转换器 @NullableTimestampConverter() DateTime? dob, }) = _UserModel; // 自定义fromJson,为空时补充当前时间 factory UserModel.fromJson(Map<String, dynamic> json) { final rawModel = _$UserModelFromJson(json); return rawModel.copyWith( dob: rawModel.dob ?? DateTime.now(), ); } }
原理说明
- 转换器作用:通过
JsonConverter注解,告诉Freezed在序列化/反序列化时,自动将Firestore的Timestamp类型与Dart的DateTime类型互相转换,无需手动修改生成代码。 - 默认值处理:由于
DateTime.now()不是编译时常量,无法直接用在@Default中,因此通过自定义fromJson工厂函数,在反序列化时补充运行时的默认值。
内容的提问来源于stack exchange,提问作者Tpp
相关产品推荐
相关产品推荐

