👌
【Flutter】freezedでフィールドに自分で作った型やDateTimeがあるとfromJsonが作れない問題
FlutterでFreezedを使ってコード生成を行っている場合、自作クラスやDateTime
をフィールドに含めてfromJson
を生成しようとすると、目的の型に正しくキャストされず失敗してしまいます。
この記事では、そのような問題の解決策を紹介します。
freezed
や一緒に使うことが多いriverpod
、state_notifier
の使い方については以下の記事をご覧ください。
問題
繰り返しになりますが下記の場合のように、freezed
で定義するフィールドに自分で作った型やDateTime
を宣言すると、fromJson
を生成したときに上手く生成してくれません。
todo_id.dart
class TodoId with _$TodoId {
const factory TodoId([
String? value,
]) = _TodoId;
}
- 使っている側はこんな感じ
todo_item.dart
class TodoItem with _$TodoItem {
const factory TodoItem({
required TodoId id,
required Title title,
required Detail detail,
required DateTime createdAt,
}) = _TodoItem;
factory TodoItem.fromJson(Map<String, dynamic> json) =>
_$TodoItemFromJson(json);
}
解決方法
下記のように、JsonConverter
を実装したクラスを作り、変換する処理を記載します。
todo_id.dart
class TodoId with _$TodoId {
const factory TodoId([
String? value,
]) = _TodoId;
}
class TodoIdConverter implements JsonConverter<TodoId, String> {
const TodoIdConverter();
TodoId fromJson(String? value) {
return TodoId(value);
}
String toJson(TodoId todoId) {
return todoId.value ?? '';
}
}
あとは、アノテーション付きで各フィールドに宣言します。
todo_item.dart
class TodoItem with _$TodoItem {
const factory TodoItem({
() required TodoId id,
() required Title title,
() required Detail detail,
() required DateTime createdAt,
}) = _TodoItem;
factory TodoItem.fromJson(Map<String, dynamic> json) =>
_$TodoItemFromJson(json);
}
これを行うと、以下のようにfreezed
で生成されるtoJson/fromJson
のメソッド内でConverter
をつかって変換してくれるようになります。
todo_item.g.dart
_$_TodoItem _$$_TodoItemFromJson(Map<String, dynamic> json) => _$_TodoItem(
id: const TodoIdConverter().fromJson(json['id'] as String),
title: const TitleConverter().fromJson(json['title'] as String),
detail: const DetailConverter().fromJson(json['detail'] as String),
createdAt:
const DateTimeConverter().fromJson(json['createdAt'] as String),
);
Map<String, dynamic> _$$_TodoItemToJson(_$_TodoItem instance) =>
<String, dynamic>{
'id': const TodoIdConverter().toJson(instance.id),
'title': const TitleConverter().toJson(instance.title),
'detail': const DetailConverter().toJson(instance.detail),
'createdAt': const DateTimeConverter().toJson(instance.createdAt),
};
ちなみに、DateTimeConverter
は以下のように実装しています。
date_time_converter.dart
class DateTimeConverter implements JsonConverter<DateTime, String> {
const DateTimeConverter();
DateTime fromJson(String json) {
return DateTime.parse(json).toLocal();
}
String toJson(DateTime dateTime) {
return dateTime.toLocal().toString();
}
}
参考
Discussion