カウントダウンタイマーがほしくて、いろいろみていたけど、
よさそうなのがなかったので、試してみたときの備忘録(*´ω`*)
ほしいもの
- start/stop/restart/resetができる
- 初期値/インターバルが設定できる
- 現在の時間が取得/listenできる
Timer.periodic
やStopwatch
もあるけど、
停止ができなかったり、途中の時間が取れなかったりしたので自作した。
できたもの
// count_down_timer.dart import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'count_down_timer.freezed.dart'; part 'count_down_timer.g.dart'; @freezed class CountDownTimerState with _$CountDownTimerState { const CountDownTimerState._(); const factory CountDownTimerState({ // カウントダウン中かどうかのフラグ required bool isRunning, // 残り時間の初期値 required Duration initial, // インターバルの間隔 required Duration interval, // 現在の残り時間 required Duration current, // 内部で使うTimer required Timer? timer, }) = _CountDownTimerState; } @riverpod class CountDownTimer extends _$CountDownTimer { @override CountDownTimerState build({ required Duration initial, required Duration interval, }) { ref.onDispose(() { state.timer?.cancel(); }); return CountDownTimerState( isRunning: false, interval: interval, initial: initial, current: initial, timer: null, ); } // ******************************************************** // * actions // ******************************************************** // カウントダウンの開始 void start() { if (state.isRunning) return; state = state.copyWith(isRunning: true, timer: _createTimer()); } // カウントダウンの停止 void stop() { if (!state.isRunning) return; // Timerを破棄する state.timer?.cancel(); state = state.copyWith(isRunning: false, timer: null); } // 残り時間のリセット: 初期値に戻す void reset() { state = state.copyWith(current: initial); } // カウントダウンのリスタート void restart() { state.timer?.cancel(); state = state.copyWith( current: initial, isRunning: true, timer: _createTimer(), ); } // ******************************************************** // * private // ******************************************************** // インターバルごとに呼び出されるTimerを作成 Timer _createTimer() { return Timer.periodic(state.interval, _onTick); } // インターバルごとに呼び出されたときの処理 void _onTick(Timer timer) { // 停止中ならなにもしない if (!state.isRunning) return; // 残り時間を計算 final current = state.current - state.interval; // 残り時間が0以下かどうかの判定 final isOver = current.compareTo(Duration.zero) < 0; if (isOver) { // 残り時間が0ならTimerを停止 state.timer?.cancel(); state = state.copyWith(current: Duration.zero, timer: null); } else { // 残り時間が0より大きい場合は、残り時間を更新 state = state.copyWith(current: current); } } }
使い方
使い方はこんな感じ
import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class CountDownTimerScreen extends HookConsumerWidget { const CountDownTimerScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // カウントダウンタイマーの準備 final myTimer = countDownTimerProvider( initial: const Duration(seconds: 3), interval: const Duration(milliseconds: 10), ); // 現在の時刻をwatch final currentTime = ref.watch(myTimer.select((value) => value.current)); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('time: ${currentTime}'), ElevatedButton( onPressed: () { ref.read(myTimer.notifier).start(); }, child: const Text("Start"), ), ElevatedButton( onPressed: () { ref.read(myTimer.notifier).stop(); }, child: const Text("Stop"), ) ], ); } }
以上!! 地味に便利。。(*´ω`*)