【Flutter】「Freezed」を学ぼう
こんにちは、Saeです。
私は「さえないエンジニア」から「さえたエンジニア」へクラスチェンジするために、日々レベル上げに勤しんでます。
今日は「Freezed」というPackageについて解説します。
また今回使用しているサンプルコードはGitHubで公開しております。参考になれば幸いです。
目次
忙しい人のための「Freezed」
日々忙しさで忙殺されているエンジニアへ向けた「Freezed」の説明です。
ここだけ見れば、なんとなくわかります。
- 「Freezed」とは、データクラスとそれに必要な機能を自動生成してくれるパッケージのこと
- 作成する時は以下のステップで行う
- パッケージをインストール
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
# freezed
freezed_annotation: 2.2.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
# freezed
freezed: 2.2.1
build_runner: 2.3.2
json_serializable: 6.5.4
- クラスを作成
import 'package:freezed_annotation/freezed_annotation.dart';
// 生成されるdartファイルを記述
part 'user.freezed.dart';
part 'user.g.dart';
// freezedでコード生成するために「@freezed」を記述
class User with _$User { // withの後には「_$[class name]」の形式で記述
// プロパティを指定
const factory User({
required int id,
required String username,
required String email,
required String phone,
(false) bool isPremium,
}) = _User;
// json形式で受け取るためのコードを生成するために記述
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
- コードを生成
flutter pub run build_runner build --delete-conflicting-outputs
- 完成
Freezedとは
それではここから「Freezed」について詳しく説明します。
そもそも「Freezed」とは何なのでしょうか。
公式ドキュメントには以下のように記載されてます。
これは、データクラス/ユニオン/パターン/マッチング/クローン作成用の、もう1つのコードジェネレーターです。
要約すると「データクラスと、それに必要な機能を自動生成してくれるパッケージ」ということです。
Freezedの使い方
では実際にデータクラスを生成するまでの流れを、コードと合わせて説明します。
なお今回は「APIからjson形式のレスポンスを格納するデータクラスを作成」という
実際によくあるケースを元に実装を進めていきます。
1. パッケージをインストール
まずは以下のURLのReadMeを元に、Freezedのパッケージをインストールしましょう。
なお今回の実装で使うpubspec.yamlの記述は以下になります。
※記述後は"pub get"を行い、パッケージをインストールすること
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
# freezed
freezed_annotation: 2.2.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
# freezed
# 開発時にしか使用しないので、「dev_dependencies」に記載
freezed: 2.2.1
build_runner: 2.3.2
# json形式のレスポンスを受け取るので、変換用に「json_serializable」もインストール
json_serializable: 6.5.4
2. クラスを作成
インストールが終わりましたら、次はデータクラスを作成していきましょう。
今回はUserのデータを受け取るためのデータクラスを作ります。
以下が実装コードです。
また以下のコードを記述すると、最初はエラーが出ますが
コード生成後は出なくなるので気にせずOKです。
user.dart
///////// a. パッケージをインポートし、自動生成されるファイルを記述 /////////
import 'package:freezed_annotation/freezed_annotation.dart';
// 生成されるdartファイルを記述
part 'user.freezed.dart';
part 'user.g.dart';
///////// b. コード生成するためのクラスを作成 /////////
// freezedでコード生成するために「@freezed」を記述
class User with _$User { // withの後には「_$[class name]」の形式で記述
// プロパティを指定
const factory User({
required int id,
required String username,
required String email,
required String phone,
(false) bool isPremium, // デフォルト値は「@Default([デフォルト値]])」の形式で指定可能
}) = _User;
// json形式で受け取るためのコードを生成するために記述
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
ではコメントの内容について、1つづつ解説します。
a. パッケージをインポートし、自動生成されるファイルを記述
まずはFreezedのアノテーションを使うために、以下のインポート文を記述しましょう。
import 'package:freezed_annotation/freezed_annotation.dart';
次はpartキーワードの後ろに、この後コード生成されるdartファイルを記載します。
part 'user.freezed.dart';
part 'user.g.dart';
各ファイルの役割は以下になります。
- [File Name].freezed.dart → Freezedで生成されるデータクラス
- [File Name].g.dart → jsonを変換する処理が生成されるクラス
なお「partって何?」と思われた方もいると思います。
参考記事のStack Overflowでは、以下のように回答されてます。
Dartでは、プライベートメンバーは同じライブラリ内でアクセス可能です。
importを使用すると、ライブラリをインポートし、そのパブリックメンバにのみアクセスすることができます。part/partofを使用すると、1つのライブラリを複数のファイルに分割し、それらのファイル内のすべてのコードでプライベートメンバーにアクセスすることができます。
importと比べると、partはプライペートなメンバーへアクセスできるようになるキーワードということみたいです。
b. コード生成するためのクラスを作成
クラスの中身を作成していきましょう。
まずは以下のようにクラスを作成します。
class User with _$User {}
ここでの実装ポイントは以下です。
- freezedでコード生成するために「@freezed」を記述
- withの後には「_$[class name]」の形式で記述
次にユーザー情報に必要なプロパティを指定していきましょう。
const factory User({
required int id,
required String username,
required String email,
required String phone,
(false) bool isPremium,
}) = _User;
ここでの実装ポイントは以下です。
- デフォルト値は「@Default([value]])」の形式で指定可能
最後にjson形式で受け取るためのコードを生成するために、以下の一文を追加しましょう。
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
3. コードを生成
これで作成に必要な準備は整いました。
最後に以下のコマンドをプロジェクト直下で実行しましょう。
flutter pub run build_runner build
※既にファイルが存在し、再生成する場合は以下のコマンドを使用(更新の場合など)
flutter pub run build_runner build --delete-conflicting-outputs
うまくいけば、以下のようにコードが自動生成されます。
Freezedで生成したデータクラスの使用方法
データクラスは作成できましたが、「実際の使い方はどうなの?」と思われる方も多いでしょう。
そこで作成したデータクラスを実際どのように使うかの実装例を見ていきましょう。
要件は以下です。
- json形式のユーザー情報をデータクラスに格納
- 格納したデータクラスを元に、画面へユーザー情報を表示
では早速実装していきましょう。
1. json形式のユーザー情報をデータクラスに格納
pubspec.yaml
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
# http(API通信用パッケージ)
http: ^0.13.5
# freezed
freezed_annotation: 2.2.0
user_request.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../model/user.dart';
class UserRequest {
// jsonplaceholderのapiを使用
Uri url = Uri.parse('https://jsonplaceholder.typicode.com/users');
Future<List<User>> getAllUser() async {
// user情報を取得する
http.Response response = await http.get(url);
if (response.statusCode != 200) {
return <User>[];
}
// 受け取ったStringのjsonレスポンスを、List形式に変換
List userMap = jsonDecode(response.body);
List<User> users = [];
userMap.forEach((user) {
// 変換したレスポンスを、Freezedで作成したUserクラスの中に入れる
// 入れる際には、fromJson(json形式の値を、Userクラスのプロパティへ代入)を使用する
users.add(User.fromJson(user));
});
// UserクラスのListを返却
return users;
}
}
2. 格納したデータクラスを元に、画面へユーザー情報を表示
freezed_user_page.dart
import 'package:flutter/material.dart';
import 'package:freezed_example/api/user_request.dart';
import 'package:freezed_example/model/user.dart';
class FreezedUserPage extends StatelessWidget {
const FreezedUserPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final userRequest = UserRequest();
List<User> userList = <User>[];
return Scaffold(
body: FutureBuilder<List<User>>(
// ①で作成した関数を実行
// ※実行が終わったタイミングでbuilderが実行
future: userRequest.getAllUser(),
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
// ①の実行結果(snapshot.data)を、userListの変数に代入
userList = snapshot.data ?? <User>[];
return ListView.builder(
itemCount: userList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
shadowColor: Colors.black,
child: Column(
// userListに入っている各値を表示する
children: [
SizedBox(
height: 10,
),
// UserName
Row(
children: [
Text(userList[index].id.toString() + ':'),
Text(userList[index].username),
],
),
SizedBox(
height: 10,
),
Row(
// Email
children: [
Text('Email:'),
Text(userList[index].email),
],
),
Row(
// Phone
children: [
Text('Phone:'),
Text(userList[index].phone),
],
),
],
),
);
},
);
},
),
);
}
}
まとめ
いかがでしたでしょうか。
Freezedを使えば最低限のソースコードで、安全・機能性の高いデータクラスを作成できることがわかったと思います。
みなさんもFreezedを活用し、素敵なFlutterライフをお過ごしくださいませ。では〜。
Discussion