🐷

[Dart] 時間経過を取得できるタイマーを作る

2021/05/15に公開

Flutterでアプリを作成していて, 「〇〇秒経過したら通知する」かつ「経過時間を通知する」タイマーがほしかったです.
なのでTimerクラスをラップして, このような機能を持つクラスを作ってみました.

import 'dart:async';

// タイマー終了時のコールバック型定義
typedef OnEndedCallback = Function();

// 定期的に経過時間を通知するためのコールバック型定義
typedef OnTickedCallback = Function(MyTimer);

class MyTimer {
  // タイマーを開始してから停止までの時間
  final Duration _timeLimit;
  
  // [_tick]毎に[_onTickedCallback]で通知を行う
  final Duration _tick;
  
  // 経過時間[_elapsed]が[_timeLimeit]以上になったときに呼ばれるコールバック
  final OnEndedCallback _onEndedCallback;
  
  // [_tick]毎に呼ばれるコールバック
  final OnTickedCallback _onTickedCallback;
  
  // 内部で使用するタイマー
  Timer? _timer;
  
  // 経過時間
  Duration _elapsed = Duration(seconds: 0);

  MyTimer(
    this._timeLimit,
    this._tick,
    this._onEndedCallback,
    this._onTickedCallback,
  );
  
  // 経過時間を取得するgetter
  Duration get elapsedTime => _elapsed; 

  // タイマーを開始するメソッド
  void start() {
    if(_timer == null){
      _timer = Timer.periodic(_tick, _onTicked);
    }
  }

  // タイマーを停止するメソッド
  // タイマーを停止した場合は[_onEndedCallback]は呼ばれない
  void stop() {
    if (_timer != null) {
      _timer!.cancel();
    }
  }
  
  // [_elapsed]を更新して[_onTickedCallback]を呼び出す
  // 終了判定も行う
  void _onTicked(Timer t) {
    this._elapsed += _tick;
    this._onTickedCallback(this);

    if (this._elapsed >= this._timeLimit) {
      _onEndedCallback();
      t.cancel();
    }
  }
}

使い方

5秒間のタイマーで1秒ごとに経過時間を通知する例です.

void main() {  
  final timer = MyTimer(
    Duration(seconds: 5),
    Duration(seconds: 1),
    () {
      print("end");
    },
    (MyTimer t) {
      print(t.elapsedTime.inSeconds);
    },
  );
  
  timer.start();
}

出力は次の通りにです. 1秒ごとにカウントアップされるように表示されます.

1
2
3
4
5
end

OnTickedCallbackに終了ロジック書いて, 条件を満たす場合にタイマーを終了させることもできます.
例えば1 ~ 5秒のうちランダムな時間まで数え上げるときはこんなふうに使えます.

import 'dart:async';
import 'dart:math';

void main() {
  final random = Random().nextInt(5) + 1;  
  final timer = MyTimer(
    Duration(seconds: 5),
    Duration(seconds: 1),
    () {
      print("end");
    },
    (MyTimer t) {
      final elapsed = t.elapsedTime.inSeconds;
      print(elapsed);
      if(elapsed >= random){
        t.stop();
      }
    },
  );

  timer.start();
}

出力例

1
2
3

Discussion