问题
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