freezed パッケージの使い方【Flutter/Dart】
Dart の freezed パッケージの使い方をすぐに理解できなかったのでまとめておきます。
freezed は Dart において、immutable なオブジェクト用のコード生成ができるパッケージです。
1. pubspec.yaml にインストール
pubspec.yaml に、パッケージを追加してインストールします。freezed は開発用のパッケージのため、dev_dependencies に追加します。build_runner も同様です。freezed_annnotation はその名の通り、@freezed 用のパッケージです。
# pubspec.yaml
dependencies:
freezed_annotation:
dev_dependencies:
build_runner:
freezed:
2. クラスを作る
さて、freezed で生成するクラスを作成してみましょう。
この時点ではまだコードにエラーが出ますが無視して大丈夫です。
続くステップでコードが生成されると、エラーは消えます。
ここでは user.dart ファイルに User というクラスを定義してみましょう。 part 'user.freezed.dart'; の一行が必要です。これは、コード生成コマンドを実行すると user.freezed.dart という名前のファイルが同階層のフォルダに生成されて、それを同じライブラリとして扱いますよ、ということを意味します。foundation パッケージのインポートは必須ではないですが、追加すると devtool で綺麗に表示されるようです。
@freezed 以下はいろいろなバリエーションがありますが、ここでは名前付きコンストラクタとして定義しています。(詳しくシンタックスを知りたい方はこちら:https://pub.dev/packages/freezed#the-syntax)。
基本的な部分としては、_$User クラスを mixin することと、_User クラスをコンストラクタに代入する必要があります。これらのクラスがこれから生成されるクラスです。
// user.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'user.freezed.dart';
@freezed
abstract class User with _$User {
const factory User({String name, int age}) = _User;
}
3. Generator を走らせる
ターミナルで以下のコマンドを実行すると自動的に @freezed を見つけ出して、コードを生成してくれます。user.freezed.dart が生成されたと思います。コードに freezed 以外のエラーがある場合はうまくいきません。
flutter pub run build_runner build --delete-conflicting-outputs
4. 使ってみる
さて、コード生成がうまく行ったのか試してみましょう。
void main() {
final user = User(name: 'katsuo', age: 12);
final user2 = user.copyWith(age: 24)
}
Warning を無視する
生成されたコードは綺麗ではないため、Warning で注意されます。無視するには、ルートレベルに analysis_options.yaml を追加して以下を記述します。
# analysis_options.yaml
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
toJson / fromJson を定義する
toJson と fromJson が必要な場面は結構多いですが、それも簡単に実装してくれます。実装方法は、part 'user.g.dart'; の一行と、
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
の一行を追加するだけです。実際使うときは User を任意のクラス名に変換してください。
// user.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
@freezed
abstract class User with _$User {
const factory User({String name, int age}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
toJson/fromJson から外して欲しい変数がある場合には、@JsonKey(ignore: true) をつけてあげれば OK です。
また、変数ごとに独自の toJson / fromJson を設定することもできます。
それには、@JsonKey(fromJson: Function, toJson: Function) のように、関数を指定すればできちゃいます。この辺の機能は、json_serializableの機能です(freezed の内部で json_serializable を使っています)。
終わりに
freezed 便利ですね〜。Dart の公式でこういう機能を提供して欲しい。
今回はめちゃめちゃ基本的なところでしたが、気が向いたら詳細なシンタックスの説明を追記していきたいです。