Freezedの使い方
freezed の準備
・詳しくは他サイト参照。
・パッケージのバージョンは各自で対応すること。
パッケージをインストールする
flutter pub add
freezed_annotation: ^2.2.0
json_annotation: ^4.8.0
pubspec.yamlへ使用するパッケージを追記する
dev_dependecies:以下へ追記する
build_runner: ^2.3.3
freezed: ^2.3.2
json_serializable: ^6.6.1
・freezedのコードを書く時、part以下の部分を記述する。
最初にエラーが出ているが、下記コマンド実行後に解消される。
part 'cardnameitem.freezed.dart'; // for freezed
part 'cardnameitem.g.dart'; // for json
・freezedのコードが書けたら、ターミナルで以下コマンドを実行する
flutter pub run build_runner build
freezedを使用した一例
・freezedでJSONを使用するにはfromJsonメソッドが必要。
・freezed3.0.0より、abstractがクラスの先頭に必要になりました。
import 'dart:convert';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'cardnameitem.freezed.dart'; // for freezed
part 'cardnameitem.g.dart'; // for json
// csv data を読み込むときに使用する
// 追記 abstract ,freezed 3.0.0より必須に
@freezed
abstract class cardNameItem with _$cardNameItem{
const factory cardNameItem({
required String cardName,
required int useDateIndex,
required int payeeIndex,
required int moneyIndex,
}) = _cardNameItem;
factory cardNameItem.fromJson(Map<String , dynamic> json) =>
_$cardNameItemFromJson(json);
}
・下に続く
上記のfreezedを使用したクラスを、
リストとして持っているState。
・リストをJSONで使用するため、下記2点を行う
◎fromJsonメソッドを追記する。
◎@JsonSerializable(explicitToJson: true)を追記する。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:cardcsvreader/cardcsvdata/cardnameitem.dart';
part 'cardnameState.freezed.dart'; // for freezed
part 'cardnameState.g.dart'; // for json
@freezed
class cardNameState with _$cardNameState{
@JsonSerializable(explicitToJson: true) // オブジェクトを入れるため、ここに一行追加
const factory cardNameState({
@Default(<cardNameItem>[]) List<cardNameItem> cardNameList,
@Default(0) int displayItemIndex,
}) = _cardNameState;
factory cardNameState.fromJson(Map<String , dynamic> json) =>
_$cardNameStateFromJson(json);
}
・ターミナルで flutter pub run build_runner build
テスト用コード
// Json
void DebugPrintJSONCardNameList(){
print( jsonEncode(state.toJson()) ); //jsonを文字列で表示する
}
void DebugReadJSONFromList(){
state = cardNameState.fromJson( jsonDecode(_testjsonCardList) ); // JSONの文字列を読み込んで使ってみる。
}
ローカル時間のDateTime をJosnで使用する
DateTimeをJsonで使用するとき、UTC時間で表示されてしまうのを修正する。
・以下のコードでDateTimeConverterを実装しておく。
misc.dartとして、流用できるようにしておくとよい。
必要なパッケージは適時読み込むこと。
// DateTime for Json
class DateTimeConverter implements JsonConverter<DateTime, String> {
const DateTimeConverter();
@override
DateTime fromJson(String json) {
return DateTime.parse(json).toLocal();
}
@override
String toJson(DateTime dateTime) {
return dateTime.toLocal().toString();
}
}
・@freezedの宣言内で DateTimeを使用する
@DateTimeConverter() required DateTime CreateTime, // DateTime用のjsonconverterを使用 misc.dart
DateTimeをFreezed・Jsonで使用するとき、
DateTimeConverterのfromJson()とtoJason()を使うようにすれば、時刻のずれが修正される。
riverpod_generator は、Flutter の状態管理ライブラリである Riverpod のプロバイダ(Provider)を自動生成するためのツールです。 [1, 2]
アノテーション(@riverpod)を付けたシンプルな関数やクラスを定義するだけで、冗長なボイラープレートコードを自動作成してくれます。 [3, 4]
簡潔な記述: 従来のように StateProvider や FutureProvider などを明示的に使い分ける必要がなく、関数の戻り値から適切な Provider が自動選択されます。
型安全性: コード生成によって型推論が強化され、実装ミスを減らせます。
パラメータの渡しやすさ: 従来の family を使った複雑な引数の受け渡しが、通常の関数引数のように記述できます。
最新の推奨手法: Riverpod 2.0 以降、公式はこの riverpod_generator を使った実装を推奨しています。 [3, 4, 5, 6, 7]
パッケージの追加: riverpod_annotation を dependencies に、riverpod_generator と build_runner を dev_dependencies に追加します。
定義の作成:
// hello.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'hello.g.dart'; // 自動生成されるファイル
@riverpod
String hello(Ref ref) => 'Hello World';
生成コマンドの実行:
dart run build_runner build
riverpod_generator では、アノテーションを付けた関数名やクラス名に基づいて Provider の名前が自動決定されるため、名前が重複するとコンパイルエラー(または生成コード内での衝突)が発生します。
名前の衝突: String myData(...) という関数を定義すると、自動的に myDataProvider という変数が生成されます。引数がある場合もない場合も、同じ myDataProvider という名前を使おうとするため、プログラム上で区別ができなくなります。
生成ルールの仕様: Riverpod のコード生成は「1つの定義(関数やクラス)に対して1つの Provider 変数」を生成する仕組みだからです。 [1]
用途に応じて、名前を分けて定義するのが一般的です。 [2]
名前を分ける例:
// 引数なし(デフォルト用など)
@riverpod
String userDefault(Ref ref) => 'Default User';
// 引数あり(family相当)
@riverpod
String userById(Ref ref, String id) => 'User: $id';
引数に初期値を持たせる(おすすめ):
riverpod_generator では、関数の引数にデフォルト値を設定できます。これにより、実質的に「引数なし」としても「引数あり」としても呼び出せるようになります。
@riverpod
String user(Ref ref, {String id = 'default'}) {
return 'User: $id';
}
// 呼び出し側
ref.watch(userProvider()); // 引数なし(defaultが使われる)
ref.watch(userProvider(id: '123')); // 引数あり