3
2

More than 3 years have passed since last update.

Flutterでカウントダウンをなるべくシンプルに実装する

Last updated at Posted at 2020-04-17

はじめに

ひょんなことからFlutterでカウントダウンするコードを書くことになったのですが、色々なサンプル実装などを見ていて以下のような引っ掛かりがありました。

  • Timer自体にはpauseするメソッドはない(カウントダウンのためのものではないため)
  • AnimationBuilderを使っているためフレームごとでリビルドが起きており、長時間を計測する場合だと動作が重くなる
  • StreamBuilderやらblocパターンを使って実装しているけど、なんだか複雑に実装しすぎている
  • かなり隅のコードなのでProviderも使うのが億劫

ということで、諸々のパッケージを使わずにカウントダウンをなるべくシンプルに実装しました。

実装

留意点としては以下です。

  • Timer自体には一時停止などの機能はないので、一時停止時には一旦破棄し、再生時に作り直す
  • 実際の計算をするのはcurrentSecondsの役割
  • currentSecondsが更新されない場合はカウントは進まない
  • つまり、1秒以内の間隔で再生・停止を連打するとカウントは進まない
  • これを進めたい場合はmilliseconds単位で実装しなおせばよいですが、あまり現実的でないのと余計なリビルドの原因となるためしない方が良いと思います
count_hoge.dart

class CountHoge extends StatefulWidget {
  CountHoge({Key key}) : super(key: key);

  @override
  _CountHogeState createState() => _CountHogeState();
}

class _CountHogeState extends State<CountHoge> {
  Timer _timer;
  int _currentSeconds;

  @override
  void dispose() {
    super.dispose();
    _timer.cancel();
  }

  @override
  void initState() {
    super.initState();
    final workMinuts = 5;
    _currentSeconds = workMinuts * 60;

    _timer = countTimer();
  }

  Timer countTimer() {
    return Timer.periodic(
      const Duration(seconds: 1),
      (Timer timer) {
        if (_currentSeconds < 1) {
          timer.cancel();
        } else {
          setState(() {
            _currentSeconds = _currentSeconds - 1;
          });
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          const SizedBox(height: 16),
          _timeStr(),
          const SizedBox(height: 16),
          _panel(),
        ],
      ),
    ));
  }

  Widget _timeStr() {
    return Text(
      timerString(_currentSeconds),
      style: TextStyle(fontSize: 32, color: Colors.black),
    );
  }

  String timerString(int leftSeconds) {
    final minutes = (leftSeconds / 60).floor().toString().padLeft(2, '0');
    final seconds = (leftSeconds % 60).floor().toString().padLeft(2, '0');
    return '$minutes:$seconds';
  }

  Widget _panel() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        GestureDetector(
          onTap: () {
            if (_timer.isActive) {
              _timer.cancel();
            }
          },
          child: SizedBox(height: 40, child: Icon(Icons.stop)),
        ),
        GestureDetector(
            onTap: () {
              if (!_timer.isActive) {
                setState(() {
                  _timer = countTimer();
                });
              }
            },
            child: SizedBox(height: 40, child: Icon(Icons.play_arrow))),
      ],
    );
  }
}

画面

スクリーンショット 2020-04-17 11.15.21.png

3
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2