flutter hooksを使ってみた感想

2023/01/26に公開

きっかけ

私は今までatomic designを使用してデザインを使用してきたのですが、最近tree designという新しい設計手法を知りました。
https://zenn.dev/mutex_inc/articles/beca85dd7fdcae#3.1.-goal
私もまだ理解はできていないのですが、atomic designとは違いPageの下にロジック(Hook)やコンポーネントを書くことでコンポーネントを予測可能なモジュールとして設計できるというもの。

それを理解する上でHooksの理解は重要だと思い今一度Hooksとはなんなのかを考えてみたのですが
この時点ではHookは再利用性を高めることができると知っているだけ....
詳しく知りたいと思い 早速試してみました。

参考にした動画

https://www.youtube.com/watch?v=A1DUBgIsCv8

作成する3つのタイマー

便宜上

  1. Statefullを使用したデフォルト
  2. UseEffectを使用したHookTimer
  3. ファイルを二つにわけ再利用性を高めたreusableHooksTimer
    に分けています。

デフォルト

StateFullWidgetを使用したごく普通のタイマーです。StateFullWidgetについて詳しく知っていれば特にいうことはありません。

//normal default Timer
import 'dart:async';
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  late Timer _timer;
  int _number = 0;
 //一秒ごとに_numberが増えていく仕組み
  
  void initState() {
    super.initState();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _number = timer.tick;
      });
    });
  }
//UI
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('title'),
      ),
      body: Center(
        child: Text(_number.toString()),
      ),
    );
  }
  //_timerを止め 安全に値を破棄する
  
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

Hooks Timer

だいぶスッキリしました。特徴はなんといってもStateFullWidgetが必要ないところ。
useState()で使用するStateを定義し useEffectで処理を書いています。

//Hooks useEffect stateful が必要ない
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

//HookWidgetを使用
class HomePageHook extends HookWidget {
  
  Widget build(BuildContext context) {
  //使用するStateをここで定義
    final _numberNotifier = useState(0);
 // Hooksの処理をここに書く
    useEffect(() {
      final timer = Timer.periodic(Duration(seconds: 1), (timer) {
        _numberNotifier.value = timer.tick;
      });
      return timer.cancel;
    }, const []);
    return Scaffold(
      appBar: AppBar(
        title: const Text('title'),
      ),
      body: Center(
        child: Text(_numberNotifier.value.toString()),
      ),
    );
  }
}

reusable_Hooks_Timer

UIとロジックを分離させることで再利用性を高めています。基本的にUIとロジックは分離させるのでこの書き方が最もHookの良さを生かしているはずです。

reusable_hooks_timer_UI.dart
//Hooks Custome 再利用可能
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_timer_app/timer/reusable_hooks/timer_hooks.dart';

class HomePageCustomHook extends HookWidget {
  
  Widget build(BuildContext context) {
  //Hooksを呼び出し
    final number = useInfiniteTimer(context);
    return Scaffold(
      appBar: AppBar(
        title: const Text('title'),
      ),
      body: Center(
        child: Text(number.toString()),
      ),
    );
  }
}
useTimerHooks.dart
//処理は基本的に一緒
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

int useInfiniteTimer(BuildContext context) {
  return use(_InfiniteTimer());
}

class _InfiniteTimer extends Hook<int> {
  
  __InfiniteTimerState createState() => __InfiniteTimerState();
}

class __InfiniteTimerState extends HookState<int, _InfiniteTimer> {
  late Timer _timer;
  int _number = 0;

  
  void initHook() {
    super.initHook();
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      setState(() {
        _number = timer.tick;
      });
    });
  }

  
  int build(BuildContext context) {
    return _number;
  }

  
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

参考

https://kimuralog.com/?p=1941
https://qiita.com/mkosuke/items/f88419d0f4d41ed6d858#useeffect

使ってみて

HookWidgetなどUIとロジックを分離させる必要のない小規模な開発ではとても有用だと感じた一方、再利用性を高めてHooksはriverpodの方が扱いやすいとこの時点では感じてしまいました。
Classを二つ使用しなくてはならなかったり、していたためです。
ただriverpodとは違い特定の値のみのロジックを作成するのにはとても扱いやすなと感じました。
どちらがいいか使い分けはまだできませんが、その時はまた書き足そうと思います。

使い分け(予定)

追記

hooksによるtextField
値を入力した後にテキストフィールドからフォーカスを外すと入力した値が消えてしまうの際は
https://qiita.com/SoarTec-lab/items/809aed85eb4253de8165?utm_campaign=post_article&utm_medium=twitter&utm_source=twitter_share
TextFiled関係の記事
https://dev-yakuza.posstree.com/flutter/widget/textfield/#textfieldの値を使う方法
https://zenn.dev/kboy/books/ca6a9c93fd23f3/viewer/14dfcb#textfieldの値を取得する(texteditingcontroller)

Discussion