Flutterストップウォッチ機能

Flutterには`StopWatch`のクラスがあって時間を計測する機能があります。単にストップウォッチとしても使えますが、作成したアプリで経過時間を計ったり活用方法は色々あります。
![スクリーンショット 2022-09-19 8.39.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/127029f4-aa06-0f67-b14a-717bfb5a5a50.png)
今回は上記のようなストップウォッチを`flutter_hooks`と`riverpod`を使用し作ってみました。
```timer.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class TimerView extends HookConsumerWidget {
  TimerView({Key? key}) : super(key: key);
  final Stopwatch timer = Stopwatch();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final timerDisplay = useState('00:00:00.000');
    const dul = Duration(milliseconds: 10);
    final isStopPressed = useState(true);
    final isResetPressed = useState(true);
    final isStartPressed = useState(true);

    void keepRunning() {
      if (timer.isRunning) {
        Timer(dul, keepRunning);
      }
      int milliSeconds = ((timer.elapsedMilliseconds / 10).floor() % 100);
      // ignore: prefer_interpolation_to_compose_strings
      timerDisplay.value = (timer.elapsed.inHours).toString().padLeft(2, '0') +
          ':' +
          (timer.elapsed.inMinutes % 60).toString().padLeft(2, '0') +
          ':' +
          (timer.elapsed.inSeconds % 60).toString().padLeft(2, '0') +
          ':' +
          (milliSeconds).toString().padLeft(3, '0');
    }

    return Scaffold(
        backgroundColor: Colors.white70,
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            textBaseline: TextBaseline.alphabetic,
            children: [
              Container(
                  height: 160.0,
                  color: Colors.white,
                  child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          "HH : MM : SS . fff ",
                          style: Theme.of(context).textTheme.headline5,
                        ),
                        Text(
                          timerDisplay.value,
                          style: Theme.of(context).textTheme.headline3,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            OutlinedButton(
                              style: OutlinedButton.styleFrom(
                                foregroundColor: Colors.black,
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(10),
                                ),
                                side: const BorderSide(),
                              ),
                              onPressed: () async {
                                isStopPressed.value = false;
                                isStartPressed.value = false;
                                isResetPressed.value = false;
                                timer.start();
                                Timer(dul, keepRunning);
                              },
                              child: const Text('スタート'),
                            ),
                            const SizedBox(
                              width: 10,
                            ),
                            OutlinedButton(
                                style: OutlinedButton.styleFrom(
                                  foregroundColor: Colors.black,
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(10),
                                  ),
                                  side: const BorderSide(),
                                ),
                                onPressed: () async {
                                  isStopPressed.value = true;
                                  isResetPressed.value = false;
                                  isStartPressed.value = true;
                                  timer.stop();
                                },
                                child: const Text('ストップ')),
                            const SizedBox(
                              width: 10,
                            ),
                            OutlinedButton(
                                style: OutlinedButton.styleFrom(
                                  foregroundColor: Colors.black,
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(10),
                                  ),
                                  side: const BorderSide(),
                                ),
                                onPressed: () async {
                                  isResetPressed.value = true;
                                  isStartPressed.value = true;
                                  isStopPressed.value = true;
                                  timer.stop();
                                  timer.reset();
                                  timerDisplay.value = '00:00:00.000';
                                },
                                child: const Text('リセット')),
                          ],
                        )
                      ]))
            ],
          ),
        ));
  }
}
```
`StopWatchView`だったり、`Stopwatch stopwatch = Stopwatch();`とネーミングをしても良かったのですが応用できるので、`timer`で失礼致します。`Stopwatch()`には、`start()` `stop()` `reset()`のメソッドがあるので、それぞれをボタンに配置しました。ミリ秒まで判定できるようにしてあります。