🪝
FlutterHooksでカスタムHookは作れるのか?
🤔Reactで作ったのを真似できないか?
これと同じものを再現してみた。
使用したパッケージ
📁フォルダ構成
ファイルは少ないので、こんな感じですね。domainがモデルクラスが書いてあり、hookがReactで言うところのカスタムHookが配置されています。
lib/
├── domain
│ └── user.dart
├── hook
│ └── hook.dart
└── main.dart
🥏モデルクラス
APIから取得するデータの型に合わせて、クラスを定義しています。
domain/user.dart
// APIから取得したデータをUserのリストに変換する
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
// factoryコンストラクタで、Userのリストに変換する
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
🪝カスタムHookを作ってみる
ReactのカスタムHookのように、自分で自作することができます。自作した関数と表現した方がいいかもですね。やることは単純で、自作した関数の中で、useStateとuseEffectを実行してるだけです。
今回は、いつものようにクラスの中にメソッドを書いてません。メソッドだけだから、見た目もReactぽいですね。
hook/hook.dart
import 'dart:convert';
import 'package:custom_hook_api/domain/user.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:http/http.dart' as http;
// カスタムフックでAPIデータを取得する関数
List<User>? useApiData() {
// useStageで、Userのリストを保持する
final data = useState<List<User>?>(null);
// useEffectで、APIからデータを取得する
useEffect(() {
// fetchData()でAPIからデータを取得し、data.valueにセットする
// async/awaitを使うと、useEffectの中で非同期処理を書くことができないので、
// thenを使って非同期処理を書く
fetchData().then((response) {
if (response.statusCode == 200) {
// APIから取得したデータをUserのリストに変換する
final jsonData = json.decode(response.body);
// data.valueにセットする
data.value =
List<User>.from(jsonData.map((item) => User.fromJson(item)));
}
});
return;// nullにするのは、useEffectが呼ばれるタイミングを一回だけにするため
}, const []); // []を書くのは、useEffectが呼ばれるタイミングを一回だけにするため
return data.value;// data.valueを返す
}
// APIからデータを取得する関数
Future<http.Response> fetchData() {
return http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
}
📡APIと通信をしてみる
カスタムHookをmain.dartで呼び出して、APIから取得したデータをUIに表示します。
main.dart
import 'package:custom_hook_api/hook/hook.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends HookWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
title: 'API Data Example',
home: CustomHook(),
);
}
}
class CustomHook extends HookWidget {
const CustomHook({Key? key}) : super(key: key);
Widget build(BuildContext context) {
// カスタムフックでAPIデータを取得する
final data = useApiData();
return Scaffold(
appBar: AppBar(
title: const Text('カスタムフックでAPIデータを取得'),
),
body: Center(
child: data != null// dataがnullでない場合
? ListView.builder(
itemCount: data.length,// dataの長さをitemCountにセットする
itemBuilder: (context, index) {
final user = data[index];// dataのindex番目の要素をuserにセットする
return ListTile(
title: Text(user.name),// userのnameを表示する
subtitle: Text(user.email),// userのemailを表示する
);
},
)
: const CircularProgressIndicator(),
),
);
}
}
実行結果
最後に
useEffectを使用したときに、async/awaitを書くことができないので、仕方なく thenメソッドを使いました。他の書き方をすればasync/awaitを使うこともできるかもしれないです。
Discussion