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))],),
);
}
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))],),
);
}