⏱️

Flutterでのタイマー機能の作り方【provider&ChangeNotifer】

9 min read

この前flutterでタイマー機能を実装しました。ストップウォッチも然り、なかなか日本語でしっかりした記事がなかったので、共有いたします。

参考になったサイト

https://youtu.be/f2FbW_D8VC8

こちらを参考にして実装しました。
細かいところは修正しましたが、大体はこんな感じです。

やっぱりflutterはGoogleが開発しているだけあって、youtubeに参考資料がたくさんあるので活用しときましょう。(個人的にはテキストの情報の方が好きですが…)

実装全コード

providerを採用しましたので、UI部分とロジック部分にファイル分割しています。

timer.dart

ChangeNotifierProvider<TimerModel>(
            create: (_) => TimerModel(),
            child: Consumer<TimerModel>(
              builder: (context, model, child) {
                return Column(
                  children: [
                    const SizedBox(height: 50.0),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            const Text('HH'),
                            NumberPicker.integer(
                                initialValue: model.hour,
                                minValue: 0,
                                maxValue: 23,
                                onChanged: (val){
                                  model.changeHourVal(val);
                                }
                            ),
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                           const Text('MM'),
                            NumberPicker.integer(
                                initialValue: model.minute,
                                minValue: 0,
                                maxValue: 59,
                                onChanged: (val){
                                  model.changeMinuteVal(val);
                                }
                            ),
                          ],
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            const Text('SS'),
                            NumberPicker.integer(
                                initialValue: model.second,
                                minValue: 0,
                                maxValue: 59,
                                onChanged: (val){
                                  model.changeSecondVal(val);
                                }
                            ),
                          ],
                        ),
                      ],
                    ),
                    const SizedBox(height: 50.0),
                    Container(
                      child: Center(
                        child: Text(
                          model.timeToDisplay,
                          style: const TextStyle(
                            fontWeight: FontWeight.w700,
                            fontSize: 30.0,
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 50.0),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        RaisedButton(
                          color: Colors.green,
                          padding: const EdgeInsets.symmetric(
                            horizontal: 40.0,
                            vertical: 10.0,
                          ),
                          child: Text(
                            'Start',
                            style: const TextStyle(
                              fontSize: 18.0,
                              color: Colors.white,
                            ),
                          ),
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(15.0),
                          ),
                          onPressed: model.started ? model.start : null,
                        ),
                        RaisedButton(
                          color: Colors.redAccent,
                          padding: EdgeInsets.symmetric(
                            horizontal: 40.0,
                            vertical: 10.0,
                          ),
                          child: Text(
                            'Stop',
                            style: const TextStyle(
                              fontSize: 18.0,
                              color: Colors.white,
                            ),
                          ),
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(15.0),
                          ),
                          onPressed: model.stopped ? model.stop : null,
                        )
                      ],
                    ),
                  ],
                );
              },
            ),
          ),
	  
timer_model.dart

class TimerModel extends ChangeNotifier {
  int hour = 0;
  int minute = 0;
  int second = 0;
  bool started = true;
  bool stopped = true;
  int timeForTimer = 0;
  String timeToDisplay = '';
  bool checkTimer = true;

  changeHourVal(val){
    this.hour = val;
    notifyListeners();
  }

  changeMinuteVal(val){
    this.minute = val;
    notifyListeners();
  }

  changeSecondVal(val){
    this.second = val;
    notifyListeners();
  }


  void start() {
    started = false;
    stopped = true;
    notifyListeners();

    this.timeForTimer = ( (this.hour * 60 * 60) + (this.minute * 60) + this.second );
    Timer.periodic(
      Duration(
      seconds: 1,
      ), (Timer t){
        if(this.timeForTimer < 1 || this.checkTimer == false){
          t.cancel();
          this.checkTimer = true;
          this.timeToDisplay = '';
          stopped = false;
          started = true;
        } else if(this.timeForTimer < 60){
          this.timeToDisplay = this.timeForTimer.toString();
          this.timeForTimer = this.timeForTimer-1;
        } else if(this.timeForTimer < 3600) {
          int m = this.timeForTimer ~/ 60;
          int s = this.timeForTimer - (60 * m);
          this.timeToDisplay = m.toString() + ':' + s.toString();
          this.timeForTimer = this.timeForTimer -1;
        } else{
          int h = this.timeForTimer ~/ 3600;
          int t = this.timeForTimer - (3600 * h);
          int m = t ~/ 60;
          int s = t -(60 * m);
          this.timeToDisplay =
              h.toString() +':'+ m.toString() +':'+ s.toString();
          this.timeForTimer = this.timeForTimer -1;
        }
        notifyListeners();
      });
  }

  void stop(){
    started = true;
    stopped = false;
    checkTimer = false;
  }

}

解説

UI部分から解説していきます。

UI

NumberPikerを使います。これを使うことでよくある感じの数字を選ぶやつになります。
詳しくは公式のドキュメントを見てください。


Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    const Text('HH'),
    NumberPicker.integer(
	initialValue: model.hour,
	minValue: 0,
	maxValue: 23,
	onChanged: (val){
	  model.changeHourVal(val);
	}
    ),
  ],
),

Model

最初に変数宣言しときます。
flutterでボタンをアクティブや非アクティブにるためには、boolの変数などで操作してあげるのが一番効率良さそうです。


int hour = 0;
int minute = 0;
int second = 0;
bool started = true;
bool stopped = true;
int timeForTimer = 0;
String timeToDisplay = '';
bool checkTimer = true;

以下では1秒おきに関数を呼び出しています。
公式のドキュメント見ときましょう。

https://api.dart.dev/stable/2.10.4/dart-async/Timer/Timer.periodic.html

残り1秒未満またはストップ状態の時、60秒未満、59分未満、それ以上って感じで数字の処理を変更しています。

ちなみにボタンのアクティブを変更させるためにnotifylisnersを2回しています。(あんまりパフォーマンス的によくないかも…誰か教えて…笑)


Timer.periodic(
	Duration(
	seconds: 1,
	), (Timer t){
	if(this.timeForTimer < 1 || this.checkTimer == false){
	  t.cancel();
	  this.checkTimer = true;
	  this.timeToDisplay = '';
	  stopped = false;
	  started = true;
	} else if(this.timeForTimer < 60){
	  this.timeToDisplay = this.timeForTimer.toString();
	  this.timeForTimer = this.timeForTimer-1;
	} else if(this.timeForTimer < 3600) {
	  int m = this.timeForTimer ~/ 60;
	  int s = this.timeForTimer - (60 * m);
	  this.timeToDisplay = m.toString() + ':' + s.toString();
	  this.timeForTimer = this.timeForTimer -1;
	} else{
	  int h = this.timeForTimer ~/ 3600;
	  int t = this.timeForTimer - (3600 * h);
	  int m = t ~/ 60;
	  int s = t -(60 * m);
	  this.timeToDisplay =
	      h.toString() +':'+ m.toString() +':'+ s.toString();
	  this.timeForTimer = this.timeForTimer -1;
	}
	notifyListeners();
	});

最後に

てな感じで実装しました。正直ここまでになると、自分でロジック1から考えるよりも前に考えた人がいないか考えたり、情報収集したほうがいいかと思います。

時間の処理の部分は自分も思い浮かばなかったので、似たコードを探したほうがいいなって思って最初の動画にたどり着きました。参考になれば幸いです。

Discussion

ログインするとコメントできます