flutter_hooksでストップウォッチを作る

flutter_hooksのHookWidgetで状態管理を行い、ストップウォッチを作ってみた!

**今回使うパッケージ**
https://pub.dev/packages/flutter_hooks

これがソースコード
```dart
import 'dart:async';

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

class StopWatch extends HookWidget {
  const StopWatch({super.key});

  @override
  Widget build(BuildContext context) {
    final timer = useState<Stopwatch?>(null);
    final elapsedTime = useState<Duration>(Duration.zero);

    useEffect(() {
      Timer.periodic(const Duration(seconds: 1), (Timer t) {
        if (timer.value?.isRunning ?? false) {
          elapsedTime.value = elapsedTime.value + const Duration(seconds: 1);
        }
      });
      return null; // useEffectのクリーンアップは不要なのでnullを返す
    }, []);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              elapsedTime.value.toString().split('.').first.padLeft(8, "0"),
              style: TextStyle(fontSize: 24),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    if (timer.value == null) {
                      timer.value = Stopwatch()..start();
                    } else {
                      timer.value?.start();
                    }
                  },
                  child: Text("開始"),
                ),
                const SizedBox(width: 10),
                ElevatedButton(
                  onPressed: () {
                    timer.value?.stop();
                  },
                  child: Text("一時停止"),
                ),
                const SizedBox(width: 10),
                ElevatedButton(
                  onPressed: () {
                    timer.value?.stop();
                    elapsedTime.value = Duration.zero;
                    timer.value = null;
                  },
                  child: const Text("完了"),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}
```

## 機能について解説
Dartの標準機能役割について解説

```dart
final timer = useState<Stopwatch?>(null);
```
この行では、現在の`Stopwatch`インスタンスを保持するための`useState`を使用しています。初期値は`null`で、これはストップウォッチが開始されるまでのデフォルトの状態を示しています。

```dart
final elapsedTime = useState<Duration>(Duration.zero);
```
`elapsedTime`は、ストップウォッチが記録した経過時間を保持するための`useState`です。初期値は`Duration.zero`（0秒）です。

```dart
useEffect(() {
  Timer.periodic(Duration(seconds: 1), (Timer t) {
    if (timer.value?.isRunning ?? false) {
      elapsedTime.value = elapsedTime.value + Duration(seconds: 1);
    }
  });
  return null;
}, []);
```
`useEffect`を使用して、コンポーネントのマウント時に1秒ごとに実行されるタイマーを設定しています。このタイマーは、`Stopwatch`が動作している場合（`timer.value?.isRunning`が`true`）、`elapsedTime`の値を1秒ずつ増加させます。このフックはコンポーネントがマウントされたときにのみ実行されるので、配列の依存関係は空です。

```dart
ElevatedButton(
  onPressed: () {
    if (timer.value == null) {
      timer.value = Stopwatch()..start();
    } else {
      timer.value?.start();
    }
  },
  child: Text("開始"),
),
```
開始ボタンの`onPressed`コールバックです。もしストップウォッチがまだ開始されていない場合（`timer.value`が`null`）、新しい`Stopwatch`インスタンスを作成し、それを開始します。既に存在する`Stopwatch`がある場合は、それを再開します。

```dart
ElevatedButton(
  onPressed: () {
    timer.value?.stop();
  },
  child: Text("一時停止"),
),
```
一時停止ボタンの`onPressed`コールバックです。ここで、ストップウォッチの`stop`メソッドを呼び出しています。これにより、ストップウォッチは一時停止されます。

```dart
ElevatedButton(
  onPressed: () {
    timer.value?.stop();
    elapsedTime.value = Duration.zero;
    timer.value = null;
  },
  child: Text("完了"),
),
```
完了ボタンの`onPressed`コールバックです。ここでは、まずストップウォッチを停止し、次に`elapsedTime`を0にリセットし、最後にストップウォッチインスタンス自体を`null`にリセットしています。これにより、完全に初期の状態に戻ります。

## 最後に
以上が、`flutter_hooks`を使用して、ストップウォッチの基本的な機能を実装したソースコードと解説でした。