【Flutter】freezedチートシート

Dart

概要

どうも、@daiki1003です!

前著JsonKeyチートシートがそこそこ好評だったでした。

【Flutter】JsonKeyチートシート
みなさま、Flutter生活はいかがでしょうか? 今回は、ある程度の規模感のアプリになると必須と言っても過言ではない json_annotationパッケージに含まれているJsonKeyについての解説記事になります。 ...

そのため、freezedも需要あるのではと思い、書いてみました!

freezed使ってるけど、使いこなせてる感じはしない
freezedのoptionって何があるかわかっていない

と言う方に向けてわかりやすく解説したいと思うのでぜひご覧いただければと思います。

執筆時環境

名前バージョン
Flutter3.7.12
freezed2.3.2

unionKey

Unionをシリアライズ/デシリアライズする際に使用するキーを指定します。
デフォルトは、runtimeTypeです。

class Union with _$Union {
  factory Union.first() = _First;
  factory Union.second() = _Second;

  factory Union.fromJson(Map<String, Object> json) => _$UnionFromJson(json);
}

とした定義があるとします。
この時、jsonをパースしようとすると

{'runtimeType': 'first'} // Union.first
{'runtimeType': 'second'} // Union.second

となるのですが、

@Freezed(unionKey: 'type')
class Union with _$Union {
  // ...
}

とすることで、

{'type': 'first'} // Union.first
{'type': 'second'} // Union.second

となります。

toJsonの出力も同様に変化します。

unionValueCase

Unionをシリアライズ/デシリアライズする際に、バリューの命名規則について指定します。

先ほどの実装を

@Freezed(unionValueCase: FreezedUnionCase.snake)
class Union with _$Union {
  // ...
}

とすることで、

{'runtime_type': 'first'} // Union.first
{'runtime_type': 'second'} // Union.second

となります。

fallbackUnion

Unionが正しくデシリアライズできなかった場合に、代わりに使用するUnionを指定します。

@Freezed(fallbackUnion: 'first')
class Union with _$Union {
  // ...
}

とすることで、

{'runtimeType': 'first'} // Union.first
{'runtimeType': 'second'} // Union.second
{'runtimeType': 'third'} // Union.first

となります。

copyWith

copyWithを生成するかどうかを指定します。
デフォルトは、trueです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、デフォルト値のtrueが採用されます。

equal

==/hashcodeを生成するかどうかを指定します。
デフォルトは、nullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、独自に==が実装されていない場合のみ
==/hashcodeが生成されます。

toStringOverride

toStringを生成するかどうかを指定します。
デフォルトは、nullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、独自にtoStringが実装されていない場合のみ
toStringが生成されます。

fromJson

fromJsonを生成するかどうかを指定します。
デフォルトは、nullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、対象のクラスがfromJson

@freezed
class Example with _$Example {
  factory Example(int a) = _Example;

  factory Example.fromJson(Map<String, Object?> json) => _$ExampleFromJson(json);
}

の様に、実装している場合のみ生成します。

逆に、以下の様に実装している場合は、生成されません。

@freezed
class Example with _$Example {
  factory Example(int a) = _Example;

  factory Example.fromJson(Map<String, Object?> json) {
    // `=>` ではなく `{ return }` を使っているため生成されない
    return {...};
  }
}

と、書いてはいるのですが、実際に何をreturnすればいいのか、現状わかっていません。
詳しい人教えてください🙏

toJson

toJsonを生成するかどうかを指定します。
デフォルトは、nullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、対象のクラスがfromJson

@freezed
class Example with _$Example {
  factory Example(int a) = _Example;

  factory Example.fromJson(Map json) => _$ExampleFromJson(json);
}

の様に、実装している場合のみ生成します。

逆に、以下の様に実装している場合は、生成されません。

@freezed
class Example with _$Example {
  factory Example(int a) = _Example;

  factory Example.fromJson(Map<String, Object?> json) {
    // `=>` ではなく `{ return }` を使っているため生成されない
    return {...};
  }
}

と、ここまで全くfromJsonと同じ説明を書いていますが間違いではありません。
fromJson/toJsonはペアの存在ですので、fromJsonの有無は
toJsonの有無に一致する様になっているのです。

map

mapmapOrNull, maybeMapメソッドを
それぞれ生成するかを指定できます。

FreezedMapOptionはコメントアウト部分を除くと以下のような定義になっています。

class FreezedMapOptions {
  const FreezedMapOptions({
    this.map, 
    this.mapOrNull, 
    this.maybeMap,
  });

  factory FreezedMapOptions.fromJson(Map json) =>
      _$FreezedMapOptionsFromJson(json);

  static const all = FreezedMapOptions(
    map: true, 
    mapOrNull: true, 
    maybeMap: true,
  );

  static const none = FreezedMapOptions(
    map: false, 
    mapOrNull: false, 
    maybeMap: false,
  );

  final bool? map;
  final bool? mapOrNull;
  final bool? maybeMap;
}

それぞれ生成するかどうかを指定できます。
デフォルトはnullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、FreezedMapOptions.all、つまり全部生成する様になります。

個人的には、mapOrNullmaybeMap
unionの良さを一つ消してしまうので、mapのみの生成が良いと思っています。

理由としては、 orElseなどの「その他」と言う指定の仕方をしてしまうと新しい要素を追加した時に
orElseに吸い込まれてしまい、コンパイルも通ってしまうため使用箇所の確認を怠ってしまう可能性があるからです。

この際、build.yamlには以下の様に記述します。

targets:
$default:
  builders:
    freezed:
      options:
        map:
          map_or_null: false
          maybe_map: false

when

whenwhenOrNull, maybeWhenメソッドを
それぞれ生成するかを指定できます。

FreezedWhenOptionはコメントアウト部分を除くと以下のような定義になっています。

class FreezedWhenOptions {
  const FreezedWhenOptions({
    this.when,
    this.whenOrNull,
    this.maybeWhen,
  });

  factory FreezedWhenOptions.fromJson(Map json) =>
      _$FreezedWhenOptionsFromJson(json);

  static const all = FreezedWhenOptions(
    when: true, 
    whenOrNull: true, 
    maybeWhen: true,
  );

  static const none = FreezedWhenOptions(
    when: false,
    whenOrNull: false,
    maybeWhen: false,
  );

  final bool? when;
  final bool? whenOrNull;
  final bool? maybeWhen;
}

それぞれ生成するかどうかを指定できます。
デフォルトはnullです。

もし、nullなら、build.yamlの値を見に行きます。
それもnullなら、FreezedWhenOptions.all、つまり全部生成する様になります。

個人的には、whenOrNullmaybeWhen
unionの良さを一つ消してしまうので、whenのみの生成が良いと思っています。

理由は、mapの方を参照してください。

この際、build.yamlには以下の様に記述します。

targets:
$default:
  builders:
    freezed:
      options:
        when:
          when_or_null: false
          maybe_when: false

makeCollectionsUnmodifiable

List, Map, Setをそれぞれ
UnmodifiableListView, UnmodifiableMapView,
UnmodifiableSetViewに変換するかを指定できます。

デフォルトはtrueです。

なんで接尾辞Viewなんだろう…。

addImplicitFinal

明示的にコンストラクタの引数パラメタにfinalを付与するかを指定できます。

デフォルトはtrueです。

@Freezed(addImplicitFinal: true) // なくても良い
class Person with _$Person {
  factory Person(String name, int age) = _Person;
}

は、以下と同義になります。

@Freezed(addImplicitFinal: false)
class Person with _$Person {
  factory Person(final String name, final int age) = _Person;
}

genericArgumentFactories

JsonSerializablegenericArgumentFactoriesの有効/無効を設定します。
デフォルトはfalseです。

fromJson(Map json) => _$ExampleFromJson(json)

fromJson(Map<String, Object?> json, T Function(Object?) fromJsonT) => _$ExampleFromJson(json, fromJsonT)

の様に変更します。

@Freezed(genericArgumentFactories: true)
class Example<T> extends _$Example<T> {
  factory Example({
    required T id,
  }) = _Example;

  factory Example.fromJson(Map<String, Object?> json, T Function(Object?) fromJsonT) => _$ExampleFromJson(json, fromJsonT);
}

の様な定義が出来る様です。

最後に

いかがでしたでしょうか?
今まで使ったことがないオプションはありましたか?

是非触ってみて、Freezedの手触り感を体験してみてださい!

誰かのお役に立てば。

Twitterフォローお願いします

「次回以降も記事を読んでみたい!」
「この辺分からなかったから質問したい!」

そんな時は、是非Twitter (@daiki1003)Instagram (@ashdik_flutter)のフォローお願いします♪

Twitterコミュニティ参加お願いします

Twitterコミュニティ「Flutter lovers」を開設しました!
参加お待ちしております😁

☕️ Buy me a coffee

また、記事がとても役に立ったと思う人は
コーヒーを奢っていただけると非常に嬉しいです!
@daiki1003

コメント

タイトルとURLをコピーしました