Closed7
freezedを勉強するスレ

- immutable`だと変更ができない
- 意図しない原因で値が変更されない
-
mutable
だと参照時、データを弄れるためエラーを生み出すかもしれない?
- 変更時にインスタンスの再作成が必要
- 手間が増える
- 作成時に動作チェックするため、classが意図通りの動作するか確認できる

セットアップ
パッケージをインストール
- freezed_annotation : freezedに関するアノテーション
- freezed : コードジェネレーター
- build_runner : コードジェネレーターを実行するツール
analysis_options.yamlの作成
analysis_options.yaml
をpubspec.yaml
と同じディレクトリに作成し、自動生成したファイルを無視するようにする。そうすることで警告が表示されなくなり、快適にコーディングできる。
analysis_options.yaml
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
errors:
invalid_annotation_target: ignore
コード生成
実行コマンド
- Flutterに依存したプロジェクトの場合
flutter pub run build_runner build
- Flutterに依存していないプロジェクトの場合
dart pub run build_runner build
生成元のファイル
生成元のファイル
// main.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'main.freezed.dart';
@freezed
class Union with _$Union {
const factory Union(int value) = Data;
const factory Union.loading() = Loading;
const factory Union.error([String? message]) = ErrorDetails;
}
-
'package:freezed_annotation/freezed_annotation.dart'
をインポートする -
main.freezed.dart
を読み込む -
@freezed
アノテーションをつける
機能
構文
基本
生成元のクラスを作成時、プロパティを宣言してはいけない代わりにfactory
を使う。
例
@freezed
class Person with _$Person {
factory Person({ String? name, int? age }) = _Person;
}
抽象クラスの活用が楽になる
抽象クラスを作成する時、abstract
が不要になる。
before
@freezed
abstract class MixedIn with Mixin implements _$MixedIn {
MixedIn._();
factory MixedIn() = _MixedIn;
}
mixin Mixin {
int method() => 42;
}
after
@freezed
abstract class MixedIn with Mixin implements _$MixedIn {
MixedIn._();
factory MixedIn() = _MixedIn;
}
mixin Mixin {
int method() => 42;
}
生成元のファイルでメソッドを定義する
freezed
にはクラスを拡張し実行する処理がデフォルトで実装していないため、プライベートコンストラクターを定義しなければならない。
メソッドを定義する
@freezed
class Person with _$Person {
const Person._(); // Added constructor
const factory Person(String name, {int? age}) = _Person;
void method() {
print('hello world');
}
}
引数のチェック
assertを使いたい場合、@Assert
で代用する。
引数のチェック
abstract class Person with _$Person {
@Assert('name.isNotEmpty', 'name cannot be empty')
@Assert('age >= 0')
factory Person({
String? name,
int? age,
}) = _Person;
}
初期値
初期値を定義する
abstract class Example with _$Example {
const factory Example([@Default(42) int value]) = _Example;
}
コンストラクタの省略
コールバックのコンストラクタを省略可能
before
future.catchError((error) => MyClass.error(error))
after
future.catchError($MyClass.error)

@Implements
freezed_annotation.dart
/// Marks a union type to implement the interface [type] or [stringType].
/// In the case below `City` will implement `GeographicArea`.
/// ```dart
/// @freezed
/// abstract class Example with _$Example {
/// const factory Example.person(String name, int age) = Person;
///
/// @Implements<AdministrativeArea<House>>()
/// const factory Example.city(String name, int population) = City;
/// }
/// ```
///
/// Note: You need to make sure that you comply with the interface requirements
/// by implementing all the abstract members. If the interface has no members or
/// just fields you can fulfil the interface contract by adding them in the
/// constructor of the union type. Keep in mind that if the interface defines a
/// method or a getter, that you implement in the class, you need to use the
/// [Custom getters and methods](#custom-getters-and-methods) instructions.
class Implements<T> {
const Implements();
}
@With
freezed_annotation.dart
/// Marks a union type to mixin the class [type] or [stringType].
/// In the case below `City` will mixin with `GeographicArea`.
/// ```dart
/// @freezed
/// abstract class Example with _$Example {
/// const factory Example.person(String name, int age) = Person;
///
/// @With<AdministrativeArea<House>>()
/// const factory Example.city(String name, int population) = City;
/// }
/// ```
///
/// Note: You need to make sure that you comply with the interface requirements
/// by implementing all the abstract members. If the mixin has no members or
/// just fields, you can fulfil the interface contract by adding them in the
/// constructor of the union type. Keep in mind that if the mixin defines a
/// method or a getter, that you implement in the class, you need to use the
/// [Custom getters and methods](#custom-getters-and-methods) instructions.
class With<T> {
const With();
}
結論
-
@Implements
: 抽象クラスをベースにし、機能拡張を実施するクラス作成時に付与する -
@With
: 具象クラスをベースにし、機能拡張を実施するクラスを作成時に付与する

partに関して
sample.dart(生成元ファイル)
part 'sample.freezed.dart';
part 'sample.g.dart';
sample.freezed.dart(生成ファイル)
part of 'sample.dart';
sample.g.dart(生成ファイル)
part of 'sample.dart';
これはsample.dart
の拡張としてsample.freezed.dart
とsample.g.dart
が呼び込まれていることを指す。
従ってpart
は親ファイルを指し、part of
は子ファイルを意味している。
ファイルをimportするときは、sample.dart
をインポートしたらsample.freezed.dart
とsample.g.dart
がセットでインポートされる。

sample.dart
@freezed
class Union with _$Union {
const factory Union(int value) = Data;
const factory Union.loading() = Loading;
const factory Union.error([String? message]) = ErrorDetails;
const factory Union.complex(int a, String b) = Complex;
factory Union.fromJson(Map<String, Object?> json) => _$UnionFromJson(json);
}
の _$Union
は、mixinを参照(or 作成)している。
sample.freezed.dart(参照元)
/// @nodoc
mixin _$Union {
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function(int value) $default, {
required TResult Function() loading,
required TResult Function(String? message) error,
required TResult Function(int a, String b) complex,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult Function(int value)? $default, {
TResult Function()? loading,
TResult Function(String? message)? error,
TResult Function(int a, String b)? complex,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function(int value)? $default, {
TResult Function()? loading,
TResult Function(String? message)? error,
TResult Function(int a, String b)? complex,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(Data value) $default, {
required TResult Function(Loading value) loading,
required TResult Function(ErrorDetails value) error,
required TResult Function(Complex value) complex,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult Function(Data value)? $default, {
TResult Function(Loading value)? loading,
TResult Function(ErrorDetails value)? error,
TResult Function(Complex value)? complex,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(Data value)? $default, {
TResult Function(Loading value)? loading,
TResult Function(ErrorDetails value)? error,
TResult Function(Complex value)? complex,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
}

sample.dart
@freezed
class Union with _$Union {
const factory Union(int value) = Data;
const factory Union.loading() = Loading;
const factory Union.error([String? message]) = ErrorDetails;
const factory Union.complex(int a, String b) = Complex;
factory Union.fromJson(Map<String, Object?> json) => _$UnionFromJson(json);
}
のData
やLoading
、ErrorDetails
とかは抽象クラスを参照(or 作成)している。
sample.freezed.dart
abstract class Loading implements Union {
const factory Loading() = _$Loading;
factory Loading.fromJson(Map<String, dynamic> json) = _$Loading.fromJson;
}

google.dev
が作成したJSONに対し、パースをはじめとした処理を自動生成するライブラリ。
このスクラップは2ヶ月前にクローズされました
ログインするとコメントできます