🪝

FlutterHooksでカスタムHookは作れるのか?

2023/07/01に公開

🤔Reactで作ったのを真似できないか?

これと同じものを再現してみた。
https://zenn.dev/joo_hashi/articles/468dd8b31469a5

使用したパッケージ
https://pub.dev/packages/flutter_hooks
https://pub.dev/packages/http

📁フォルダ構成

ファイルは少ないので、こんな感じですね。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