Flutter Freezedとjson_serializable

`Freezed`で`Model`を自動生成するとイミュータブル(状態を変えることができないオブジェクト)でスッキリしたコードを書くことができます。`Model`クラスを作成するのに`DartのMap`(Jsonのハッシュ)を単位毎に一つずつ作る必要があり、定義したあと`json_serializable`(コードジェネレータ)を走らせれば、`JSON`の`デコード処理`(fromJsonメソッド)と`エンコード処理`(toJsonメソッド)によって自動生成が行われます。`Json`の文字列を`Dart`のデータ型で扱うためにはただの文字列だと使えないのでデコードして`Map`のクラス型にする必要があります。[実際にコードにして試してみたのがこちら!](https://qiita.com/ume-bar/items/0b8086a46608fdd8e823)

**コンストラクタ(メンバ変数を初期化する作業)やプロパティ(属性)を定義することに加え、**
```
@override で上書きできるメソッド

// オブジェクトを文字列で表現してくれる。静的(parse)な関数int.parseなどが一緒になったクラスを文字列としてテキストに表現する
.toString（）

// 二つの値を比較して等しければ「真」（true）を、等しくなければ「偽」（false）を返す
operator(演算子) ==　（等価演算子） 

// オブジェクト毎に存在し、== で比較して同じであればtrueを返す
.hashCode

```

```
// nullを割り当てれるオブジェクトを複製できるメソッド
.copyWith

// Mapクラス内のjsonを解析してデコード処理
.fromJson

// Mapへのエンコード処理
.toJson
```
**などのメソッドも一緒に定義できるので慣れてしまえばとても役に立つと思います。**

## 必要なパッケージ

```pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  freezed_annotation: ^X.X.X (freezedのアノテーション)
  freezed: ^X.X.X
  build_runner: ^X.X.X (コードジェネレーターの実行)
  json_annotation: ^X.X.X
  json_serializable: ^X.X.X (デコード・エンコードの処理)
```
## 生成したファイルと同じディレクトリに内に作成し警告を無くす処理

```analysis_options.yaml
analyzer:
  exclude:
    - "**/*.g.dart"
    - "**/*.freezed.dart"
  errors:
    invalid_annotation_target: ignore
```
## コードジェネレーターでビルドするコマンド

**Flutterプロジェクト**
```
flutter pub run build_runner build
```
生成されたファイルが既に存在し、コンフリクトエラーになってしまう場合は
```
flutter pub run build_runner build --delete-conflicting-outputs
```

**Dartプロジェクト**
```
dart pub run build_runner build
```
## [ドキュメントにあるコードで検証](https://pub.dev/packages/freezed)
```test.dart
// This file is "main.dart"
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';

// required: associates our `main.dart` with the code generated by Freezed
part 'test.freezed.dart';
// optional: Since our Person class is serializable, we must add this line.
// But if Person was not serializable, we could skip it.
part 'test.g.dart';

@freezed
class Person with _$Person {
  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;

  factory Person.fromJson(Map<String, Object?> json)
      => _$PersonFromJson(json);
}
```
## 詳細
- `test.dart`ファイルを作成し`Person`クラスをコーディング
- `part 'test.freezed.dart';` と`part 'test.g.dart';`のファイルを作成
- インスタンス化(初期値の設定)する際に、新しいインスタンスを必ずしも返えさなくて良い`factory`コンストラクタを採用
- プロパティを必須項目にする場合は型の前に`required`を置く(null非許容)

- `fromJson`でJSON相互変換用の`factory`コンストラクタを作成
### ジェネレーターを実行↓
![スクリーンショット 2022-09-22 12.52.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/0cfe9092-5ac0-302c-660f-2f26b93181d3.png)
`test.dart`ファイルの並びに`test.freezed.dart`ファイルと`test.g.dart`ファイルが生成されました。

## その他機能

- モデルで変更可能なプロパティを定義したい場合`@freezed`注釈を`@unfreezed`に変える
```dart
@unfreezed
class Person with _$Person {
  factory Person({
    required String firstName,
    required String lastName,
    required final int age,
  }) = _Person;

  factory Person.fromJson(Map<String, Object?> json)
      => _$PersonFromJson(json);
}
```
- null許容型(元の型の値の変わりにnullの値を取れる型)
```dart
String? firstName,
```
- デフォルト値を設定
```dart
@Default('ume') String firstName,
```
- `@JsonKey`でメンバ変数名を変更する
```dart
@JsonKey(name: "first_name") String firstName,
```
- 非推奨で定義したい場合
```dart
@deprecated bool isName,
```

- `Assert`（メソッド外に定義されているメンバ変数`"{"X"}"`の条件を定義）を利用し入ってきた値が空の場合はエラーを出す
```dart
abstract class Person with _$Person {
  @Assert('firstName.isNotEmpty', 'firstNameプロパティはnullを引数に渡せません')
  @Assert('lastName.isNotEmpty', 'lastName cannot be empty')
  @Assert('age >= 0')
  factory Person({
    String? firstName,
    String? lastName,
    int? age,
  }) = _Person;
}
```