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))],),
);
}
Author by
Tom Cajot
Updated on December 25, 2022Comments
-
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 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
Timer
s for the sameItem
s? -
Tom Cajot over 3 yearsNo, 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 over 3 yearsYes, every time you call
widget.model.displayCountdown()
, it will create a new periodicTimer
object. It won't affect existingTimer
s, but you'll have more running than you expect, so you'll observe more callbacks being fired. If your intention is to have oneTimer
perItem
, I recommend that you maintain aMap<Item, Timer>
so that you can avoid creating a newTimer
if one already exists (or so that you can cancel existing ones). -
Tom Cajot over 3 yearsThanks, but I found an alternative x).
-