😸

【Flutter】Freezedとは?なぜ必要?

に公開

ドキュメントを参考にしながらなぜ必要であるのかの部分を先に記載してから、簡単なモデルを用いて挙動を確認していきます。

https://pub.dev/packages/freezed

サンプルコード

https://github.com/muranakar/freezed_sample

Freezed とは?なぜ必要?

Freezed はデータクラスを簡単に作成できる Dart のコード生成ライブラリです。

Freezed とは?

Freezed は、Flutter でデータクラスを簡単に作成するためのライブラリです。通常、データクラスを作るには以下の実装が必要です:

  • コンストラクタ
  • プロパティ
  • toString()
  • == と hashCode
  • copyWith()メソッド
  • JSON 変換処理

Freezed を使えば、これらを少ないコードで自動生成できます。

なぜ Freezed が必要か?

データクラスを作成する際、次の要素が特に重要です:

コンストラクタとプロパティ

データクラスの基本的な構成要素です。オブジェクトの作成と状態の保持に必須です。プロパティがないとデータを保存できず、コンストラクタがないとオブジェクトを作成できません。

toString()

デバッグやログ出力時に重要です。適切に実装しないと、オブジェクトの内容が「Instance of 'MyClass'」のような意味のない文字列として表示され、開発時のデバッグが困難になります。

==演算子と hashCode

オブジェクトの同一性を判断するために必要です。これらを適切に実装していないと、同じ値を持つオブジェクトでも異なるものとして扱われます。特にコレクションでの検索やフィルタリング時に問題が生じます。また、HashMap や Set などのデータ構造で正しく機能するためには hashCode の実装が必須です。著者自身はこのあたりの理解が乏しいため、今後深堀りしていきたいです。

copyWith()メソッド

イミュータブル(不変)なオブジェクトを扱う際に重要です。オブジェクトの一部のプロパティだけを変更した新しいインスタンスを作成できます。これにより、状態管理が簡単になり、副作用が減少します。

JSON 変換処理

サーバーとの通信やデータの永続化において不可欠です。適切な JSON 変換がないと、API からのデータをアプリで使用できなかったり、アプリの状態を保存できなかったりします。手動で実装すると間違いやすく、メンテナンスも大変です。

インストール方法

Freezed を使うためには、まず必要なパッケージをプロジェクトに追加します。

flutter pub add freezed_annotation
flutter pub add dev:build_runner
flutter pub add dev:freezed
# JSONシリアライズが必要な場合、以下も追加します
flutter pub add json_annotation
flutter pub add dev:json_serializable

基本的な使い方

以下に、Person クラスを例にして、Freezed の使い方を説明します。

// person.dart
import 'package:freezed_annotation/freezed_annotation.dart';

// 以下の2行は自動生成ファイルをインポートします
part 'person.freezed.dart';
part 'person.g.dart';

// @freezedアノテーションでクラスを定義します

class Person with _$Person {
  // ファクトリコンストラクタを定義します
  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;

  // JSONからオブジェクトを生成するためのファクトリメソッド
  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
}

コードの生成

上記のファイルを作成した後、以下のコマンドを実行して自動生成ファイルを作成します:

dart run build_runner build

このコマンドにより、person.freezed.dartperson.g.dartという 2 つのファイルが生成されます。

Freezed の実際の使用例

以下に、生成された Person クラスを使う例を示します:

// Personの作成
// Personクラスのインスタンスを生成
final person = Person(
 firstName: '太郎',
 lastName: '川飯',
 age: 30,
);

// プロパティへのアクセス
// Freezedが生成したgetterでプロパティ値を取得
print(person.firstName); // 太郎
print(person.lastName); // 川飯

// toString()の使用
// Freezedが自動生成したtoString()メソッドでオブジェクトの内容を表示
print(person); // Person(firstName: 太郎, lastName: 川飯, age: 30)

// 等価性の比較
// 同じプロパティ値を持つ別のPersonインスタンスを作成
final samePerson = Person(
 firstName: '太郎',
 lastName: '川飯',
 age: 30,
);
print(person == samePerson); // true (同じプロパティ値を持つため)

// copyWithの使用
// firstNameだけを変更した新しいインスタンスを作成
final activePerson = person.copyWith(firstName: "ちいかわ");
print(activePerson); // Person(firstName: ちいかわ, lastName: 川飯, age: 30)

// JSONへの変換
// toJson()メソッドでオブジェクトをJSON形式に変換
final json = person.toJson();
print(json); // {"firstName":"太郎","lastName":"川飯","age":30}

// JSONからオブジェクトの作成
// fromJson()メソッドでJSONからオブジェクトを再構築
final personFromJson = Person.fromJson(json);
print(personFromJson == person); // true (同じプロパティ値を持つため)

上記を実行した場合のコンソール

flutter: 太郎
flutter: 川飯
flutter: Person(firstName: 太郎, lastName: 川飯, age: 30)
flutter: true
flutter: Person(firstName: ちいかわ, lastName: 川飯, age: 30)
flutter: {firstName: 太郎, lastName: 川飯, age: 30}
flutter: true

まとめ

Freezed を使うことで、以下のメリットがあります:

  1. ボイラープレートコードの削減:toString()、==、hashCode、copyWith などを手動で実装する必要がなくなります
  2. イミュータブルなデータモデル:不変のデータモデルを簡単に作成できます
  3. 簡単な JSON 変換:json_serializable と統合されています

Freezed を使うことで、プロジェクトの実装コード量を減らしながら、バグを減らし、メンテナンス性を向上させることができます。

Discussion