6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[flutter] flutter_hooksの使い方を学ぶ。

Posted at

はじめに

今回はflutter_hooksについて勉強したので、共有したいと思います!

正直今まで、Riverpodで代替できるし別に使わなくてもいいのでは..
的な曖昧な理解をしていたので、この機に勉強してみした。

この記事で学べること

  • flutter_hooksの使い方
  • flutter_hooksを使うシーン
  • riverpodとの使い分け

導入

flutter_hooks

Riverpodを導入していない場合は、基本的にこのpackageを使用します。

hooks_riverpod

もうすでに、flutter_riverpodを導入している場合、
flutter_riverpodの代わりにこのpackageを導入することができます。

※このpackageを導入する際には、
flutter_hooksも同時にimportする必要があります。

注意
このpackageを導入するとflutter_riverpodはimportしなくてもriverpodの機能が使えるようになります。

flutter_hooksとは

flutter_hooksのpackageを簡潔に説明すると、
アプリの状態管理や、ライフサイクルの処理を、
簡潔にわかりやすく扱うためのpackageになっています。

Riverpodとの使い分け

変数等の状態を管理するだけなら別に、
Riverpodでよくねって思ってました(真顔)

そもそもRiverpodでは、
initStateやdisposeのようなライフサイクルを実現できないですし(※)、

それを踏まえた上で、flutter_hooksの状態管理は、
widget内の状態管理に特化した
ものですので、

Riveerpodでの状態管理は,
globalに複数の画面で変数の状態管理が必要な場合に採用し、

Widget内で完結するローカルな状態管理はflutter_hooksで処理し、
使い分けるのが一般的
です。

※(ConsumerStatefulWidgetを使用すれば実現可能ですが、
statefulwidgetを使用しない形での実現は出来ないという意)

flutter_hooksの使い方

flutter_hookは、statefulwidgetやstatelesswidget,consumerwidgetなどの代わりにHookWidgetを継承します。

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // フックの使用
    return Container();
  }
}

注意
riverpodの機能も同時に使いたい場合は、hooks_riverpodをimportした上で、HookConsumerWidgetを使用します。

Hooks機能一覧

今回はPrimitivesと呼ばれる基本的なHooksの機能を紹介します。

  • useEffect

void useEffect(
 Dispose? effect(),
 [List<Object?>? keys]
)

useEffectにはList型ののkeysという引数が設定されており、
このkeysに値を設定すると、その引数の状態に変化があるたびに、
userEffectの処理が走る仕組み
になっています。

さらに、もう一つの引数であるeffectは、Dispose?型なので、
returnで返り値を指定する必要がありますが、
クリーンアップ関数と言われており、widgetが破棄されたタイミングで実行されます。

useEffectひとつでstatefulWidgetのinitStateと、
disposeのライフサイクルを実現できる
ので、
hooksの機能の中でも使用頻度が多い機能です。

以下の使用例のように、keysの配列を空で設定し、
クリーンアップ関数もnullで実装すれば、
画面が構築されたタイミングでのみ処理を走らせることも可能です。

useEffect(() {
      print('画面が構築されたよ');

      // クリーンアップ関数はここでは必要ありません
      return null; 
    }, []); 
  • useState

riverpodでいう所のproviderのようなもので、変数を生成し、
その変数の状態が変化した際にその状態変更を感知してUIが更新されます。

userStateはbuild内で定義して、そのbuild内で使用するのが一般的です。

以下の例のように初期値を定義して使用することもできますし、
型を指定すれば、初期値を省略することができます。

class Counter extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final counter = useState(0);

    return GestureDetector(
      // automatically triggers a rebuild of the Counter widget
      onTap: () => counter.value++,
      child: Text(counter.value.toString()),
    );
  }
}
  • useMemoized

T useMemoized<T>(
T valueBuilder(),
[List<Object?> keys = const <Object>[]]
)

計算した結果をキャッシュ化(保存)してくれ、
重い処理や計算を複数回実行する際などに、
前回の計算結果を利用することで、
無駄なレンダリングを抑えることで、パフォーマンスの向上を期待できます。

final test = useMemoized(() => calu(a + b), []);

上記のような書き方で、keysの配列を空で設定すると、
calu()という関数は、初回の一度のみ実行され、
二度目以降は、calu()は実行されず、前回の計算結果をそのまま出力する形になります。

final test = useMemoized(() => calu(a + b), [a,b]);

上記のように、keysの配列に,計算に使用する変数を定義しておくと、
aとbの値に変化があった時にのみ、calu()が再実行されます。

  • useCallback

useMemoizedと似ていて、計算をキャッシュ化してくれるHookです。
useMemoizedとの違いは、useMemoizedが値をキャッシュするのに対して、
関数自体をキャッシュ化するのがuseCallbackの役割です。

実際以下のように具体例を提示してみましたが、
ただのカウンターアプリにuseCallbackを採用するのは明らかに、
オーバーエンジニアリングですし、
いつどのタイミングで検討するのかというのが一番肝になりそう。

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    // useCallbackを使用してイベントハンドラーをメモ化
    final incrementCounter = useCallback(() {
      count.value++;
    }, [count.value]); // 依存関係はcount.value

    return Scaffold(
      appBar: AppBar(title: Text('useCallback Example')),
      body: Center(
        child: Text('Count: ${count.value}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementCounter, // メモ化された関数を使用
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
  • useRef

userRefは、useStateと同じく変数を定義でき状態を管理できるものですが、
useStateとは異なり、userRefで定義したものの状態が変化しても
UIの再描画が発生することはありません。

したがって、変数の状態管理はしたいが、
以下のコードのようなUIを再描画させる必要のないシーンなどで、使用します。

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // useRefを使用して参照を作成
    final myRef = useRef<int>(0);

    return ElevatedButton(
      onPressed: () {
        // ボタンを押すたびに参照の値を増加させる
        myRef.value++;
        print('Current value is: ${myRef.value}');
      },
      child: Text('Increment'),//UIの変化(再描画)はない。
    );
  }
}
  • useContext

widgetのbuildContextにアクセスするための機能です。

ActionSheetやMediaQueryでサイズを指定する際など、
contextを使用する際に、contextを継承して、、みたいなことをやることが多いですが、
useContextを使用することで直接buildContextにアクセスすることができます。

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // useContextを使用してBuildContextを取得
    final buildContext = useContext();

    // コンテキストを使用してテーマデータを取得
    final theme = Theme.of(buildContext);

    return Scaffold(
      appBar: AppBar(title: Text('useContext Example')),
      body: Center(
        child: Text(
          'Current Theme: ${theme.brightness}',
          style: theme.textTheme.headline6,
        ),
      ),
    );
  }
}
  • useValueChanged

useValueChangedは、定義した変数の状態が変化した際に、
その状態変化を契機に任意のロジックを実行するためのHookになります。

以下の例では、countの値が変化した際に、
print('Count has changed to: ${count.value}');が実行されます。

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    // useValueChangedを使用してcount.valueの変更を検出
    useValueChanged(count.value, (_, __) {
      print('Count has changed to: ${count.value}');
      // ここで副作用を実行する
      // 例: データのフェッチ、外部リソースのクリーンアップなど
    });

    return Scaffold(
      appBar: AppBar(title: Text('useValueChanged Example')),
      body: Center(
        child: Text('Count: ${count.value}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

参考資料

終わりに

ちゃんと勉強するとかなり便利で使い勝手良さそうと思いました。

最後までご覧いただきありがとうございます。
何かご指摘等あればコメントいただけると幸いです。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?