【问题标题】:Create a countdown timer that prints a remaining time every 5 seconds for each value in a list创建一个倒数计时器,每 5 秒为列表中的每个值打印一次剩余时间
【发布时间】:2018-11-01 21:31:29
【问题描述】:

我正在尝试在 Flutter 中创建一个倒计时计时器,它每 5 秒打印一次剩余时间,直到计时器用完,然后为列表中的下一个值运行相同的倒计时计时器价值观。

以下递归函数接近,但它会等待“迭代”,然后再转到 List 中的下一个值,即使该 Timer 的剩余时间较少。

import 'dart:async';

Future main() async {
  final _sequence = [21, 10, 8];
  final _iteration = 5;

  void _countdown(seq) async {
    if (seq.length > 0) {
      var duration = seq[0];
      print('Starting at $duration');
      Timer.periodic(Duration(seconds: _iteration), (timer) {
        var remaining = duration - timer.tick * _iteration;
        if (remaining >= 0) {
          print('$duration, $remaining');
        } else {
          timer.cancel();
          _countdown(seq.sublist(1));
        }
      });
    }
  }

  _countdown(_sequence);
}

每次运行的持续时间由_sequence 列表中的值定义。

即使使用CountdownTimer(而不是Timer.periodic)也存在当剩余值小于迭代时等待太久的问题:

import 'package:quiver/async.dart';

main() {
  final _sequence = [21, 10, 8];
  final _iteration = 5;

  void _countdown(seq) {
    if (seq.length > 0) {
      var duration = seq[0];
      print('Starting at $duration');
      CountdownTimer countdownTimer = CountdownTimer(
        Duration(seconds: duration),
        Duration(seconds: _iteration),
      );

      var sub = countdownTimer.listen(null);
      sub.onData((timer) {
        if (timer.remaining > Duration(seconds: 0)) {
          print('$duration, ${timer.remaining.inSeconds}');
        } else {
          sub.cancel();
          _countdown(seq.sublist(1));
        }
      });
    }
  }

  _countdown(_sequence);
}

结果应如下所示,行间停顿 5 秒,除非另有说明:

Starting at 21
21, 16
21, 11
21, 6
21, 1          <-- This one should only pause 1 second before continuing
Starting at 10
10, 5
10, 0          <-- This one should immediately continue
Starting at 8
8, 3           <-- This one should only pause 3 seconds before continuing

【问题讨论】:

  • 你能解释一下计时器什么时候用完吗?你的持续时间是多少
  • 我会回答,一旦我更好地理解你想要达到的目标

标签: dart flutter


【解决方案1】:

Dart/Flutter 支持使用 CountDownTimer 倒计时。下面是 2 个示例,一个没有 UI,一个在屏幕中心有一个简单的 Text(超时 = 10 秒,步长 = 1 秒,您可以随意更改)

示例 1:

import 'package:quiver/async.dart';

void main() {
  const timeOutInSeconds = 10;
  const stepInSeconds = 2;
  int currentNumber = 0;

  CountdownTimer countDownTimer = new CountdownTimer(
      new Duration(seconds: timeOutInSeconds),
      new Duration(seconds: stepInSeconds));

  var sub = countDownTimer.listen(null);
  sub.onData((duration) {
    currentNumber += stepInSeconds;
    int countdownNumber = timeOutInSeconds - currentNumber;
    // Make it start from the timeout value
    countdownNumber += stepInSeconds;
    print('Your message here: $countdownNumber');
  });

  sub.onDone(() {
    print("I'm done");

    sub.cancel();
  });
}

示例 2:

import 'package:flutter/material.dart';
import 'package:quiver/async.dart';

void main() {
  runApp(new MaterialApp(home: new CountdownTimerPage()));
}

class CountdownTimerPage extends StatefulWidget {
  @override
  CountdownTimerPageState createState() => new CountdownTimerPageState();
}

class CountdownTimerPageState extends State<CountdownTimerPage> {
  final timeOutInSeconds = 10;
  final stepInSeconds = 2;
  int currentNumber = 0;

  CountdownTimerPageState() {
    setupCountdownTimer();
  }

  setupCountdownTimer() {
    CountdownTimer countDownTimer = new CountdownTimer(
        new Duration(seconds: timeOutInSeconds),
        new Duration(seconds: stepInSeconds));

    var sub = countDownTimer.listen(null);
    sub.onData((duration) {
      currentNumber += stepInSeconds;

      this.onTimerTick(currentNumber);
      print('Your message here: $currentNumber');
    });

    sub.onDone(() {
      print("I'm done");

      sub.cancel();
    });
  }

  void onTimerTick(int currentNumber) {
    setState(() {
      currentNumber = currentNumber;
    });
  }

  @override
  Widget build(BuildContext context) {
    int number = timeOutInSeconds - currentNumber;
    // Make it start from the timeout value
    number += stepInSeconds;
    return new Scaffold(
      body: new Center(
          child: new Text(
        "Your message here: $number",
        style: new TextStyle(color: Colors.red, fontSize: 25.0),
      )),
    );
  }
}

【讨论】:

  • 我喜欢这种方法。如何访问“剩余”时间?目标是打印剩余时间,而不是打印 numberOfMessage。
  • 我在这里创建了一个示例:github.com/nextfunc/flutter-playground。请检查并告诉我
  • @Simpler 我也刚刚更新了我的答案。如果有帮助,您可以投票并接受。 :)
  • 感谢您的帮助,但是当增量 (stepInSeconds) 不是 1 秒时,此方法不起作用。将增量设置为 2。所需的输出为:10、8、6、4、2。
  • 哈哈,对不起。我又更新了一次代码。 @更简单
【解决方案2】:

试试这个!

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'NonStopIO',
      theme: new ThemeData(
        primarySwatch: Colors.red,
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _sequence = [10, 10, 8];

  @override
  void initState() {
    super.initState();
    startTimer(0);
  }

  startTimer(int index) async {
    if (index < _sequence.length) {
      print("Sequence $index - countdown :  ${_sequence[index]} ");
      new Timer.periodic(new Duration(seconds: 5), (timer) {
        if ((_sequence[index] / 5) >= timer.tick) {
          print("${_sequence[index] - timer.tick * 5}");
        } else {
          timer.cancel();
          startTimer(index + 1);
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('NonStopIO'),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'Counter',
            ),
            new Text(
              '',
            ),
          ],
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

【讨论】:

    【解决方案3】:
    // Start the periodic timer which prints something every 5 seconds
    Timer timer = new Timer.periodic(new Duration(seconds: 5), (timer) {
      print('Something');
    });
    
    // Stop the periodic timer using another timer which runs only once after specified duration
    new Timer(new Duration(minutes: 1), () {
      timer.cancel();
    });
    

    【讨论】:

    • 很好,但是一个单独的计时器来取消第一个计时器似乎很奇怪。
    • 据我所知,这将是解决您问题的最佳和简单的解决方案。
    • 我可以访问剩余时间以便在打印语句中打印吗?
    • 给我一分钟,我会试着弄清楚是否有其他方法
    【解决方案4】:

    我找到了另一个我认为满足您要求的答案。试试这个,

    import 'package:flutter/material.dart';
    import 'package:quiver/async.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => new _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
    
      @override
      void initState() {
    
        new CountdownTimer(new Duration(minutes: 1), new Duration(seconds: 5)).listen((data) {
          print('Something');
          print('Remaining time: ${data.remaining.inSeconds}');
        });
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return new Container(
        );
      }
    }
    

    【讨论】:

    • 好像和我上面的解决方案类似:)
    • 对不起。我刚才看了你的回答。这不是故意的。
    • 为什么 ${data.remaining.inSeconds} 不是在 CountdownTimer 中定义的增量?在这个例子中,我希望看到它从 60、55、50、45 等开始倒计时,但它比较零星。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 2021-12-03
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多