Flutter Timer Issue

465

I don't know why it didn't worked but I found an alternative that works.

First, I create a getter for my countdowns. Second, when I get my items, I set each timer in my getter. Third, I create my timers with my getter values. Fourth, I call my getter at the index of the current tile in my view.

...
  List<int> get countDowns => _durations;
  List<int> _durations = [];

  List<Item> items = [];
...

  Future getItems() async {
    setState(ViewState.Busy);
    final response = await _api.getItems();
    if (response is SuccessState) {
      items = response.value;
      for (var item in items) {
        _durations.add(item.remainingTime);
      }
      displayCountdowns();
    } else if (response is ErrorState) {
      String error = response.msg;
      print('Error $error');
    } else {
      print('Error');
    }
    setState(ViewState.Idle);
  }
...
  void displayCountdowns() {
    for (var countDown in _durations) {
      Timer.periodic(const Duration(seconds: 1), (timer) { 
        countDown -= 1000;
        _durations[0] = countDown;
        notifyListeners();
      });
    }
  }

Here is the code for the view:

...
var duration = Duration(milliseconds:widget.model.countDowns[index]);
...
  ListTile _tile(Lead lead, Duration duration) {
    String countdown = '${duration.toString().split('.')[0]}';
    return ListTile(
      leading: CircleAvatar(
        backgroundImage: lead.author["avatarUrl"] == "" ? NetworkImage('https://picsum.photos/250?image=3') : NetworkImage(lead.author["avatarUrl"]),
      ),
      title: leadTitle(lead.author["fullName"]),
      subtitle: leadSubtitle(lead.keywords.toString()),
      trailing: Row(mainAxisSize: MainAxisSize.min, children: [Text('$countdown', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey))],),
    );
  }
Share:
465
Tom Cajot
Author by

Tom Cajot

Updated on December 25, 2022

Comments

  • Tom Cajot
    Tom Cajot over 1 year

    I implemented a Timer but it doesn't seem to be calling the callback() functions after the right duration, it starts by calling the callback after a few seconds and speeds up every time the callback is called. Ex. 5 sec --> callback(), 4 sec --> callback(), 2 --> callback(), 0.3 sec --> callback () ...

    I would like to have a timer that reduces the value of an int after each second.

    Current code - ViewModel:

      void displayCountdown(Item item) {
        timer = Timer.periodic(const Duration(seconds: 1), (Timer _timer) => {
          if (lead.remainingTime >= 1) {
            item.remainingTime = item.remainingTime -= 1,
            notifyListeners(),
          }else {
            print('out of time'),
            _timer.cancel(),
            notifyListeners(),
          }
        });
      }
    

    Code - View:

      ListTile _tile(Item item) {
        widget.model.displayCountdown(item);
        Duration duration = Duration(milliseconds: item.remainingTime);
        String countdown = '${duration.toString().split('.')[0]}';
        return ListTile(
          leading: CircleAvatar(
            backgroundImage: item.author["avatarUrl"] == "" ? NetworkImage('https://picsum.photos/250?image=3') : NetworkImage(lead.author["avatarUrl"]),
          ),
          title: Text(item.author["fullName"]),
          subtitle: Text(item.keywords.toString()),
          trailing: Row(mainAxisSize: MainAxisSize.min, children: [Text('$countdown', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey))],),
        );
      }
    
    • jamesdlin
      jamesdlin over 3 years
      "it starts by calling the callback after a few seconds and speeds up every time the callback is called" ... Are you sure that you aren't creating multiple Timers for the same Items?
    • Tom Cajot
      Tom Cajot over 3 years
      No, I don't think so. I'm not really used to flutter yet. Maybe it creates a new one every time I call widget.model.displayCountdown(lead); and therefore it accelerates it? The only place where I create a Timer is in displayCountdown().
    • jamesdlin
      jamesdlin over 3 years
      Yes, every time you call widget.model.displayCountdown(), it will create a new periodic Timer object. It won't affect existing Timers, but you'll have more running than you expect, so you'll observe more callbacks being fired. If your intention is to have one Timer per Item, I recommend that you maintain a Map<Item, Timer> so that you can avoid creating a new Timer if one already exists (or so that you can cancel existing ones).
    • Tom Cajot
      Tom Cajot over 3 years
      Thanks, but I found an alternative x).