Freezed: 導入 使い方 - Ki-Kobayashi/flutter_wiki GitHub Wiki

🟩 パッケージの導入 【👇公式ページ】

https://pub.dev/packages/freezed#install

  • json_serializable
      👉 シリアライズ・デシリアライスに特化したパッケージ:Json変換に必須

.

🟡 pubspec.yaml

fvm flutter pub add freezed_annotation
fvm flutter pub add json_annotation
fvm flutter pub add --dev build_runner
fvm flutter pub add --dev freezed
fvm flutter pub add --dev json_serializable

◆公式

	dependencies:
	  # https://pub.dev/packages/freezed_annotation 👈追加
	  freezed_annotation: ^2.0.3
          # https://pub.dev/packages/json_annotation/install  👈追加
          json_annotation: ^4.8.1
	  
	dev_dependencies:
		…
	  # https://pub.dev/packages/build_runner/install  👈追加
	  build_runner: ^2.1.11
	  # https://pub.dev/packages/freezed/install  👈追加
	  freezed: ^2.0.3+1
	  # https://pub.dev/packages/json_serializable 👈追加
	  json_serializable: ^6.2.0 

image

.

🟩 analysis_options.yaml に以下追加

analyzer:
  plugins:
    - custom_lint 👈 riverpod_lintを有効にする
  exclude:
    - "**/*.g.dart"
    - "**/*.freezed.dart"
  errors:
    invalid_annotation_target: ignore 👈Analysisからfreezedが生成したコードを除外し、warningだらけを防ぐ(※json_serializable 未使用なら、不要) 

.

🟩 【VSCode】Freezedスぺニット追加

@freezedと打つと、Freezedコードが自動追記されるようにする

  1. ctrl + SHIFT + P ( or [表示]→[コマンドパレット] )
  2. 「Configure User Snippets」と入力
  3. 「dart.json」を選択
  4. 以下を追記する

🟡 dart.json

{
	// Place your snippets for dart here. Each snippet is defined under a snippet name and has a prefix, body and
	// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
	// same ids are connected.
	// Example:
	// "Print to console": {
	//  "prefix": "log",
	//  "body": [
	//    "console.log('$1');",
	//    "$2"
	//  ],
	//  "description": "Log output to console"
	// }
	"Freezed class template": {
	  "prefix": "@freezed",
	  "description": "Create a Freezed class",
	  "body": [
		  "import 'package:freezed_annotation/freezed_annotation.dart';",
		  "import 'package:flutter/foundation.dart';",
		  "",
		  "part '${TM_FILENAME_BASE}.freezed.dart';",
		  "part '${TM_FILENAME_BASE}.g.dart';",
		  "",
		  "@freezed",
		  "class ${1:DataClass} with _$${1:DataClass} {",
		  "\tconst factory ${1:DataClass}({",
		  "\t\t@Default(0) int counter, // Add your fields here",
		  "\t}) = _${1:DataClass};",
		  "",
		  "\tfactory ${1:DataClass}.fromJson(Map<String, dynamic> json) =>",
		  "\t\t_$${1:DataClass}FromJson(json);",
		  "}"
	  ]
	},
  }

.

🟡 使い方

ファイルを作成し、「@freezed」と入力すると自動補完で登録したコードが反映される

.

🟩 【AndroidStudio】Freezedスぺニット追加

@freezedと打つと、Freezedコードが自動追記されるようにする

1.File/configuration/Editor/Live Templates
  → Flutter → 右上の+ → LiveTemplate
2.以下登録
 ・Abbreviation→@freezed
    入力補完する時の文字列。自分で分かりやすい名前を命名
 ・Description
    テンプレートの説明。自分で分かりやすい説明を入力
 ・Template Text
    以下のテンプレートを入力

import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part '$FILE_NAME$.freezed.dart';
// Json用の生成コード
part '$FILE_NAME$.g.dart';
// モデルの定義
@freezed
class $CLASS_NAME$ with _$$$CLASS_NAME$ {
  // 自動生成される_$Userはmixinなので拡張ができず、そのままだと、メソッドを追加できない。。
  // 独自のメソッドを利用できるよう、単一のプライベートコンストラクタを追記する必要がある。
  const User._(); // メソッドを追加しないなら削除(privateコンストラクタ)
  const factory $CLASS_NAME$({}) = _$CLASS_NAME$;
 factory $CLASS_NAME$.fromJson(Map<String, dynamic> json) => _$$$CLASS_NAME$FromJson(json);
}

3.Edit variables 押下し、以下のように登録
  FILE_NAME
     Expression
       fileNameWithoutExtension()
     Default value
       fileNameWithExtension()
     Skip if defined
       checked

  CLASS_NAME
     Expression
       underscoresToCamelCase(String)
     Default value
       capitalize(underscoresToCamelCase(fileNameWithoutExtension()))
     Skip if defined
       checked

  1. 下のdefine直下のChange押下
    → Dart/Top-level だけにチェック

.

🟡 使い方

ファイルを作成し、「@freezed」と入力すると自動補完で登録したコードが反映される

.

🟩 【補足】VSCodeの便利プラグインを使用する

image

下記のショートカットで、コードの補完が効くようになる

🛑一番下のUnionは、Dart3で導入された sealed class で賄えるので、今後は不要

image

.

🟩 Freezedは、「Dto(Request / Response)」「状態管理するためのモデル(State)」の作成に使用できる

  • APIのDTOは通常、変更されないことが望ましい。
      👉 Freezedを使用することで、生成されたクラスは不変で再代入不可能なものとなり、意図しない変更が防げられる

  • FreezedはEquatableの機能を提供しており、等価性の確認が容易。
      👉これは、APIのResponseオブジェクトなどで特に重要

🟡【例】

  • APIの DTO (Request, Response) を作成する場合
       👉 Keyが変数名と異なるときは、「@JsonKey(name: 'xxx_xxx')」を使用する
       👉 APIのレスポンスが一部の情報を含まない場合:フィールドが存在しない場合でもエラーが発生せず、デフォルト値が利用

  • Riverpodで管理する State 作成する場合
       👉 データがないとき用に、**「@Default('')」**を使用する

  • Dto 兼 Riverpodで管理する State 作成する場合
       👉 データがないとき用に、**「@JsonKey(name: 'xxx_xxx')」&「@Default('')」**を使用する

.

🛑ネストしたクラスがシリアライズされるようにするには、「@JsonSerializable(explicitToJson: true)」をコンストラクタの上に記述する

これを記述しないと、Json変換が上手くできずに、エラーになる
.

import 'package:freezed_annotation/freezed_annotation.dart';

part 'article.freezed.dart';
part 'article.g.dart';

@freezed
class Article with _$Article {
 // 👇内部にtoJsonが必要なオブジェクトがネストしている場合、これをつけて呼び出してもらうようにする 
 🛑@JsonSerializable(explicitToJson: true) 
  const factory Article({
    @Default('') @JsonKey(name: 'article_id') String articleId,
    @Default('') String published,
    @Default('') String title,
    @Default('') String description,
    @Default('') @JsonKey(name: 'image_alt') String imageAlt,
    @Default('') String category,
    @Default('') @JsonKey(name: 'src_url') String srcUrl,
    @Default([]) @JsonKey(name: 'article_comments') List<Comment> articleComments // 👈toJsonが必要なオブジェクトがネスト
  }) = _Article;

  factory Article.fromJson(Map<String, dynamic> json) =>
      _$ArticleFromJson(json);
}

.

🛑ネストされる側は、コンストラクタの上にアノテーション不要

import 'package:freezed_annotation/freezed_annotation.dart';

part 'article_comment.freezed.dart';
part 'article_comment.g.dart';

@freezed
class ArticleComment with _$ArticleComment{
  const factory ArticleComment({
    @Default('') @JsonKey(name: 'comment_id') String commentId,
    @Default('') String description,
    ・・・
  }) = _ArticleComment;

  factory ArticleComment.fromJson(Map<String, dynamic> json) =>
      _$ArticleCommentFromJson(json);
}

.

🟩 コードの生成

生成コマンドを実行する前に、以下の点が相違がないかを必ず確認する。

  • part 行のファイル名が一致しているか?
  • Part行が、「xxx_xxx.g.dart」「xxx_xxx.freezed.dart」の2つがあるか
  • クラス名に該当する箇所が、6箇所あるか(例:Articleなら、それが6箇所)

上記の確認が終わったら、以下のコマンドをVSCodeのターミナルで実行する

🟡自動生成コマンド

下記の"いずれか"のコマンド

💡コマンド実行した時のみ生成
fvm flutter pub run build_runner build --delete-conflicting-outputs

💡変更があれば常時生成
fvm flutter pub run build_runner watch -d

@freezed が付与された作成ファイルと同じ階層に、以下2つのファイルが生成されれば成功

  • xxx_xxx.g.dart
  • xxx_xxx.freezed.dart

以降、補足情報

.

🟩 factoryコンストラクタとは

🟡factoryの使用2つ

💎 シンプルな facotry コンストラクタ

class MyClass {
  factory MyCLass() {
    // Do something...(MyClass型のインスタンスを返す)
  }
}

.

💎 facotry コンストラクタ + 拡張関数

class MyClass {
  factory MyCLass.fromJson(Map<String, dynamic> json) {
    // JsonコードからMyClassのインスタンスを生成する
  }
}

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

⚠️ **GitHub.com Fallback** ⚠️