Create a countdown timer that prints a remaining time every 5 seconds for each value in a list

独自空忆成欢 提交于 2021-01-28 12:20:06

问题


I'm trying to create a countdown timer in Flutter that prints the remaining time every 5 seconds until the timer runs out, and then runs the same countdown timer for the next value in a list of values.

The following recursive function gets close, but it waits the "iteration" before going to the next value in the List even when there is less time remaining for that 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);
}

Each time it runs the duration is defined by the value in the _sequence List.

Even using the CountdownTimer (instead of Timer.periodic) has the same problem with waiting too long when the value remaining is less than the iteration:

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);
}

The results should look like this with a 5 second pause between lines except where noted:

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

回答1:


Dart/Flutter supports countdown with CountDownTimer. Below are 2 examples, one without UI and one with a simple Text at center of screen (The timeout = 10 seconds and the step = 1 seconds, you can change to whatever you want)

Example 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();
  });
}

Example 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),
      )),
    );
  }
}



回答2:


Try this!

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:


I found another answer which satisfies your requirements i think. Try this,

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(
    );
  }
}


来源:https://stackoverflow.com/questions/50479114/create-a-countdown-timer-that-prints-a-remaining-time-every-5-seconds-for-each-v

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!