Freezed: 導入 使い方 - Ki-Kobayashi/flutter_wiki GitHub Wiki
https://pub.dev/packages/freezed#install
- json_serializable
👉 シリアライズ・デシリアライスに特化したパッケージ:Json変換に必須
.
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
.
analyzer:
plugins:
- custom_lint 👈 riverpod_lintを有効にする
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
errors:
invalid_annotation_target: ignore 👈Analysisからfreezedが生成したコードを除外し、warningだらけを防ぐ(※json_serializable 未使用なら、不要)
.
@freezedと打つと、Freezedコードが自動追記されるようにする
- ctrl + SHIFT + P ( or [表示]→[コマンドパレット] )
- 「Configure User Snippets」と入力
- 「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」と入力すると自動補完で登録したコードが反映される
.
@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
- 下のdefine直下のChange押下
→ Dart/Top-level だけにチェック
.
ファイルを作成し、「@freezed」と入力すると自動補完で登録したコードが反映される
.
下記のショートカットで、コードの補完が効くようになる
🛑一番下のUnionは、Dart3で導入された sealed class
で賄えるので、今後は不要。
.
-
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('')」**を使用する
.
これを記述しないと、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
以降、補足情報
.
class MyClass {
factory MyCLass() {
// Do something...(MyClass型のインスタンスを返す)
}
}
.
class MyClass {
factory MyCLass.fromJson(Map<String, dynamic> json) {
// JsonコードからMyClassのインスタンスを生成する
}
}
.
.
.
.
.
.