I’m trying to code a countdown timer showing the hours minutes and seconds remaining.
I’ve been trying to set the time to 24h60min60s first and it was working perfectly. But since I’m trying with 10hours it doesn’t show 9h59min59s but 059h59min59s, I’ve been trying for hours to solve it but I don’t understand how to do it as I’ve been starting coding really recently. I don’t understand how to manage this error.
If you could bring me your help guys, It would be handsome.
class Page extends StatefulWidget {
const Page({super.key});
@override
State<Page> createState() => _PageState();
}
class _PageState extends State<Page> {
int seconds = 60;
int minutes = 60;
int hours = 10;
String secText = '00';
String minText = '00';
String hText = '10';
Timer? _timer;
@override
void initState() {
super.initState();
_handleTimer();
}
void _handleTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
if (hours >= 10) {
hours--;
minutes--;
seconds--;
} else {
if (minutes == 60) {
minutes--;
}
if (minutes >= 60) {
minutes--;
seconds--;
} else {
if (seconds == 60) {
minutes--;
}
seconds--;
}
// if (minutes >= 1) {
//minutes--;
// seconds--;
//} else {
//if (seconds == 60) {
// minutes--;
// }
// seconds--;
// }
if (seconds > 9) {
secText = '$seconds';
} else {
if (seconds > 0) {
secText = '0$seconds';
} else {
seconds = 60;
secText = '00';
}
}
if (minutes > 9) {
minText = '$minutes';
} else {
if (minutes > 0) {
minText = '0$seconds';
} else {
secText = '00';
if (seconds == 60) {
_timer?.cancel();
}
}
}
if (hours > 9) {
hText = '$hours';
} else {
if (hours > 0) {
hText = '0$minutes';
}
}
}
});
});
}
2
Answers
The problem lies in this code part:
You pass minutes instead of hours.
Change that to
hours
and everything should work as you expect.Basically you don’t need to track seconds, minutes and hours separately. You can write a function, that will handle that for you:
I haven’t looked at your code too thoroughly, but there are a number of things that immediately look wrong:
Your logic is wrong:
This is the first thing your timer callback checks. I don’t know why you’re decrementing every field if the number of hours happens to be two digits. For example, this would mean that if the remaining time is 12h34m56s, your new time would be 11h33m55s. What you instead mean to do is to decrement the seconds unconditionally and then decrement the minutes only if necessary (i.e., the decremented seconds value becomes negative and needs to be wrapped to 59) and similarly decrement the hours only if necessary (i.e., the decremented minutes value becomes negative and needs to be wrapped to 59).
You shouldn’t be using or checking for 60. There is no minute 60 or second 60 on a normal clock. They’re 0. What is "24h60m60s" supposed to mean? Is that 25 hours and 1 minute, or did you intend for it to represent 24 hours? By using and checking for 60, you’re making your code unnecessary complicated by making more cases to test for, and it’s confusing.
In general, your code is unnecessarily complex. A simpler approach that is far less error-prone would be to just keep track of the total number of seconds. Then, every second, decrement that and then compute the values for the hours, minutes, and seconds components. Dart even has a class to do this for you:
Duration
.Your code assumes that timer events will fire exactly 1 second apart, but that’s not necessarily true. For example, if your system is overloaded, it might not be able to fire timer events when expected. Conceivably some timer events could get dropped entirely. A more accurate approach would be have a
DateTime
member that keeps track of when the timer is expected to end, and then on each timer callback, compute the duration betweenDateTime.now()
and that end time. (However, if you do this, you’d probably also want to round to the nearest second since code takes non-zero time to execute, and timer events can’t fire exactly 1 second apart. See https://stackoverflow.com/a/67138516/ for how to round.)Your code can leak
Timer
objects. You cancel the periodicTimer
object only when your time reaches a certain point. (The logic for that also does not make sense.) You create aTimer
ininitState
; by symmetry you should ensure that it’s cancelled in yourState
‘sdispose
method.Putting it all together, you instead should do something like: