skip to Main Content

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


  1. The problem lies in this code part:

    if (hours > 9) {
                hText = '$hours';
              } else {
                if (hours > 0) {
                  hText = '0$minutes';
                }
              }
    

    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:

    String _formatTime(int time) {
      return time < 10 ? '0$time' : '$time';
    }
    
    Login or Signup to reply.
  2. I haven’t looked at your code too thoroughly, but there are a number of things that immediately look wrong:

    1. Your logic is wrong:

             if (hours >= 10) {
               hours--;
               minutes--;t
               seconds--;
             } else {
      

      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).

    2. 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.

    3. 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.

    4. 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 between DateTime.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.)

    5. Your code can leak Timer objects. You cancel the periodic Timer object only when your time reaches a certain point. (The logic for that also does not make sense.) You create a Timer in initState; by symmetry you should ensure that it’s cancelled in your State‘s dispose method.

    Putting it all together, you instead should do something like:

    class _PageState extends State<Page> {
      DateTime endTime = DateTime.now() + const Duration(hours: 24);
    
      String secText = '';
      String minText = '';
      String hText = '';
      Timer? _timer;
    
      @override
      void initState() {
        super.initState();
        _timer = Timer.periodic(
          const Duration(seconds: 1),
          (_) => _onTimer(),
        );
    
        // Call immediately to update with initial values.
        _onTimer();
      }
    
      @override
      void dispose() {
        _timer?.cancel();
      }
    
      void _onTimer() {
        var timeRemaining = endTime.difference(DateTime.now());
    
        // Round to the nearest second.
        timeRemaining += const Duration(milliseconds: 500);
    
        if (timeRemaining <= Duration.zero) {
          timeRemaining = Duration.zero;
    
          _timer?.cancel();
          _timer = null;
          return;
        }
    
        hText = timeRemaining.inHours.toString();
        minText = (timeRemaining.inMinutes % 60).toString();
        secText = (timeRemaining.inSeconds % 60).toString();
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search