💬
【Flutter】Freezedでデータクラス(モデル)を作成
はじめに
Flutterでのアプリ開発において、堅牢なデータクラス(モデル)の設計は避けて通れません。しかし、手動で copyWith や == のオーバーライド、JSON変換などを実装するのは、非常に手間がかかり、バグの温床にもなりがちです。
そこで登場するのが、今やFlutter開発のデファクトスタンダードとも言えるコード生成ツール「Freezed」です。本記事では、Freezedの基本的な導入方法から、json_serializable と組み合わせた実践的なAPI通信の実装までを、コード例を交えて解説します。
そもそも Freezed とは?
一言で言うと、「データクラス(モデル)を最強にするためのコード生成ツール」です。
Dartで通常のクラスを作ると、以下のような面倒な作業(ボイラープレート)が発生します。
- すべてのプロパティに
finalをつける(不変性) - コンストラクタを書く
-
toStringをオーバーライドする(デバッグ用) -
operator ==とhashCodeをオーバーライドする(値の比較用) -
copyWithメソッドを手動で実装する(一部だけ値を変更した複製を作るため)
Freezedを使えば、これら全てを数行のコードを書くだけで自動生成してくれます。
シンプルな利用例
導入
flutter pub add freezed_annotation
flutter pub add json_annotation
flutter pub add --dev freezed
flutter pub add --dev json_serializable
flutter pub add --dev build_runner
pubspec.yaml
dependencies:
freezed_annotation: ^3.1.0
dev_dependencies:
json_serializable: ^6.11.2
build_runner: ^2.10.4
freezed: ^3.2.3
データモデルファイルを作成
lib/models/data.dart
import 'package:freezed_annotation/freezed_annotation.dart';
// '元ファイル名.freezed.dart'
part 'data.freezed.dart';
// JSON変換(json_serializable)を使う場合
part 'data.g.dart';
// Freezedで生成対象(abstractかseeled)
abstract class Data with _$Data {
// コンストラクタで定義したプロパティが、生成されるクラスのフィールドになる
const factory Data({
required String id,
required String title,
}) = _Data; // リダイレクトコンストラクタ
// = _Data; の部分は、ボイラープレート(決まり文句)。
// JSON(Map)をクラスに変換するためのメソッド
// これを書くと、freezedが裏側で json_serializable を呼び出す
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
// Map<String, dynamic> toJson() => ... は書きません
// fromJsonを書いた時点で、Freezedが自動的に toJson も生成してくれます
// 自分で書くと競合してエラーになります。
}
コード生成を実行
dart run build_runner watch
データクラスを使ってみる
インスタンス生成とプロパティ利用例
final newData = Data(id:'999', title:'some_title');
print(newData.id);
print(newData.title);
==で同値比較の例(プロパティの値で比較)
final newData = Data(id:'999', title:'some_title');
print(newData == Data(id:'999', title:'some_title')); // true
copyWithでオブジェクトの一部を更新
final newData = Data(id:'999', title:'some_title');
final updateData = newData.copyWith(id: 'AAA');
print(updateData.id); // AAAに更新されている
通信でJSONのやりとり例(fromJson, toJson)
import 'package:dio/dio.dart';
import 'package:performance_flutter/models/data.dart';
/// DIOインスタンス
final dio = Dio();
/// GETリクエスト
Future<List<Data>> fetchItems() async {
final res = await dio.get<List>('http://localhost:3000/datas');
// List<dynamic> -> List<Data>に変換
final dataList =
res.data!.map((el) {
return Data.fromJson(el);
}).toList();
return dataList;
}
/// POSTリクエスト
Future<void> postData(Data newData) async {
final res = await dio.post(
'http://localhost:3000/datas',
data: newData.toJson(),
);
print(res.data);
}
おわりに
本記事では、Flutter開発における強力なパートナーである「Freezed」について、導入からJSON連携までを解説しました。
改めてFreezedのメリットを振り返ると、以下の点が挙げられます。
- 圧倒的なコード量の削減: 面倒なボイラープレート記述から解放され、本質的なロジックの実装に集中できます。
- 堅牢性の向上: デフォルトでイミュータブル(不変)かつ、値による等価性比較(Value Equality)が保証されるため、予期せぬバグを防げます。
-
安全なデータ操作:
copyWithによる安全なコピー生成や、JSON変換の自動化により、開発効率が劇的に向上します。
最初は build_runner を実行する手間を少し感じるかもしれませんが、一度この快適さを知ると、手動でデータクラスを書くスタイルには戻れなくなるはずです。特にAPI通信を伴う規模のアプリでは、必須級のツールと言っても過言ではありません。
Discussion