狐好きぷろぐらまー

狐好きプログラマーのブログです。

【flutter_hooks】flutter_hooksパッケージのサンプルを書いてみた【Flutter】

こんにちは。pregum_foxです。

今回はFlutterのパッケージの1つであるflutter_hooksパッケージについてサンプルを書いてみました。

以下目次です。

背景

なぜ今頃flutter_hooksを調べることになった背景としては、仕事で使うことになりそうだったので、riverpodは使ったことがあるのですが、flutter_hooksはReact HooksのFlutter版ということだけ知っていたのですが、実際には関数コンポーネントで書けないし、どういった書き方になるのか疑問だったので調べました。

検証環境

項目 バージョン
flutter 3.13.7
flutter_hooks ^0.20.3

個人的な感想

個人的な感想しましては、以下の条件を満たす開発者だけで利用するのであれば積極的に導入したいなと言う感想です。

  • React Hooksに慣れ親しんでいる人
  • Flutterもある程度理解している人
    • ここで言うある程度は「StatefulWidgetとStatelessWidgetの違いがわかる程度」の認識です。

入れると状態を保持するWidgetを実装する際に記述量が減りますがが、そのためには開発者がReact Hooksを理解しておかないといけないというのが正直ハードルが高いかなと思いました。 また、完全に同じではなくuseStateは戻り値が配列ではなくValueNotifier型なので、~~.valueで値を取り出したりする必要があります。

色々書きましたが、dispose処理や非同期処理などは可読性が上がる為、個人で実装するプログラムでは積極的に取り入れていきたいなと思いました。

flutter_hooksとは

pub.devのURLを以下に記載致します。

flutter_hooks | Flutter Package

flutter_hooksとは、簡単に言うと~~ControllerなどのController系のクラスやsetStateメソッドで状態を変える変数などの記述量を減らす為のパッケージです。

以下pub.devの該当パッケージの概要から引用です。

A Flutter implementation of React hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

Hooks are a new kind of object that manage the life-cycle of a Widget. They exist for one reason: increase the code-sharing between widgets by removing duplicates.

↓ 日本語訳

React フックの Flutter 実装: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

フックは、 のライフサイクルを管理する新しい種類のオブジェクトですWidget。これらが存在する理由は 1 つあり、重複を削除することでウィジェット間のコード共有を増やすためです。

サンプル

以下のリポジトリにて、標準のみとflutter_hooksパッケージを利用したものの2つのWidgetを同じ機能を実装してみました。

github.com

機能としては、いつものカウントアップ機能にリセット機能を追加したWidgetを作成しました。

  • カウントアップ機能
  • カウントリセット機能

デモ動画はリポジトリのREADME.mdに記述しています。

標準ライブラリとの比較

以下にサンプルプログラム内で、標準とHooksを使用した場合の差異がある箇所を抜き出しました。

flutter_hooksを使用した場合

class HooksFizzBuzzPage extends HookWidget {
  // :

  @override
  Widget build(BuildContext context) {
    final count = useState(0);  // useStateはValueNotifierを返す
  // :

   // リセットはuseEffectを使用してリセット時に更新をかけるようにしている
    useEffect(
      () {
        debugPrint('count reset! $resetToken');
        count.value = 0;
        return null;
      },
      [resetToken.value],
    );

 Text('count.value: ${count.value}'), // 値を取得する場合は、~~.valueで取得可能
  // :

  floatingActionButton: FloatingActionButton(
  child: const Icon(Icons.add),
  onPressed: () => count.value += 1, // これで状態が更新される
  ),
  // :

}

標準の場合

class NormalFizzBuzzPage extends StatefulWidget { // StatefulWidgetが必要
// :

}

class _NormalFizzBuzzState extends State<NormalFizzBuzzPage> { 
  var count = 0; // メンバ変数として定義が必要
// :

  // 更新処理はinitStateに書く必要がある(buildメソッド内だと) 描画時に再実行されるため
  @override
  void initState() {
    super.initState();
    resetToken.addListener(() {
      debugPrint('count reset! $resetToken');
      count = 0;
    });
  // :
  }

  // disposeも同様
  @override
  void dispose() {
    resetToken.dispose();
    super.dispose();
  }
  // :

    @override
  Widget build(BuildContext context) {
    return Scaffold(
     // :

            Text('count: $count'), 
    // :

        floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () => setState(() => count += 1), // 画面に表示する変数の更新はsetStateで更新をかける必要がある。
      ),

    // :
  }

StatefulWidgetからStatelessWidget(HookWidget)に変わっているので、記述量が減っています。 簡単なサンプルでも10数行減っていて2割ぐらい減っているので、プロダクトコードであれば、もしかすると100〜1000行ぐらいは削減できるかもしれません。 またsetStateを記述しなくなっている為、可読性も上がっていて良いです。

標準の方はValueNotifier使ってないから厳密には異なるよねと感じる方もいるかと思いますので、そちらはリポジトリvalue_notifier_fizzbuzz.dart ファイルとして記述しておりますので、ぜひクローンして確認してみてください。

試してみた感じたメリット・デメリット

メリット

簡単なカウントサンプルを作ってみて感じたメリットを記述します。

  • 記述量が減る
    • Hooksを使用することでStatefulWidgetクラスをStatelessWidgetクラスに置き換えることができ、記述量が減り可読性が上がりました。
    • 〜〜Controllerなどのオブジェクトについてはflutter_hooksを使用する場合、生成から廃棄処理まで全てbuildメソッドの中に記述できる為、記述量が減って良いなと感じました。
    • setStateを記述が不要になった分、必要なコードだけが残るようになって可読性が上がりました。

デメリット

  • React Hooksの知識が必要
    • これは仕方がないのですが、React Hooksについて知識がないと何がどうつながっているか把握しにくいので、その場合は一度React Hooksについて学習することをお勧めします。 ただ、こちらのライブラリを使わないと開発ができないというわけではないので、正直優先度は低いと思います。
  • 内部で何が行われているか知らないと思わぬバグやパフォーマンス低下につながる可能性がある
    • これはフレームワークや便利なライブラリ全般に言えることですが、裏では色々やってくれている分、フレームワーク・ライブラリが意図しない書き方をしていると、思わぬパフォーマンス低下を招いたり、最悪アプリがクラッシュするなどの恐れがあるため、ドキュメントやIssueなどで躓きやすい箇所は事前に学んでおくことが必要だと思います。

よく使いそうな関数まとめ

ここでは公式で紹介されていたメソッドで、個人的によく使いそうなものをピックアップしました。

  • useState
  • useEffect
  • useMemorized
  • useFuture
  • useStream
  • useScrollController
  • useTextEditingController

flutter_hooks | Flutter Package

から引用

まとめ

長くなりそうなので3行にまとめました。

  • StatefulWidgetからStatelessWidgetに置き換えられて、コード記述量が減る。
  • その分Hooks(≒React Hooks)についての理解が必要になる
  • 上記のトレードオフを許容できるなら、導入したいパッケージである。

雑感

パッケージの表面を触ろうとしただけでも、自分の頭だと理解に時間がかかったので、内部まで理解しようとするとすごい時間が溶けそうだなと思いました🫠 ただ、読んだ分色々な書き方に触れることができると思うので、モチベーションが高まった時は中身を読んでいきたいと思います。 真面目に理解しようとすると、厳密には異なる実装になっていたりしてどうまとめようかすごい悩みましたが、自分の理解の範囲で似せてサンプルを作ったつもりです。 もし意味合いが異なる箇所がありましたら教えていただけますと幸いです。

ここまで読んでいただいてありがとうございました。

参考URL

Flutter の 状態管理で Riverpod が公式の推奨には至っていない理由 | knmts.com

5分でわかるReact Hooks #JavaScript - Qiita

Flutter HooksのuseXXXの使い方 #Flutter - Qiita