Timer does not stop - flutter

Problem
I'm doing a chat. I want the program to try to get messages again every 5 seconds if there are no messages.
My solution
I'have created a Timer in my stateful widget.
Timer timer;
When I build a widget, I check for messages. If there are none, I start the timer. I want the timer to stop at the next check, in case of existing messages
void onBuild() {
if (state.messages.isEmpty) {
_checkEmptyMessages();
timer = Timer.periodic(
Duration(seconds: 5), (Timer t) => _checkEmptyMessages());
}
}
void _checkEmptyMessages() {
print('MES789 ${state.messages.isEmpty}');
if (state.messages.isEmpty) {
add(ChatEventLoadFirstPage()); // This adds an event to the BLoC
} else {
if (timer != null) timer.cancel();
timer = null;
}
}
Also I've tried
I've tried to remove timer = null; and await for timer.cancel();, but it didn't help.
Actual output
So in the Debug Console I get this every 5 seconds:
I/flutter (13387): MES789 false
I/flutter (13387): MES789 false
I/flutter (13387): MES789 false
I/flutter (13387): MES789 false
Question
How can I stop the Timer?

Because 'Timer.periodic' is called, new Timer instance is created and stored same timer variable.
It means that not canceled timer's instance will be lost when 'Timer.periodic' is called.
So you need to check whether Timer instance is exist.
void onBuild() {
if (state.messages.isEmpty) {
if (timer == null) {
timer = Timer.periodic(
Duration(seconds: 5), (Timer t) => add(ChatEventLoadFirstPage()));
}
} else {
if (timer != null) timer.cancel();
timer = null;
}
}

this code is dead code, it will never work because you have if (state.messages.isEmpty) before _checkEmptyMessages() and if (state.messages.isEmpty) again, try to remove first if (state.messages.isEmpty)

Related

How to cancel sub Timer inside nested Timer?

I am having a problem with cancelling one nested Timer. These are the code example I have:
import 'dart:async';
void main() async{
final timer =
Timer(const Duration(seconds: 1), () {
print('Timer 1');
Timer(const Duration(seconds:1),(){
print('Timer 2');
});
});
Timer(Duration(milliseconds: 1500),(){
timer.cancel();
print('timer cancelled');
});
}
The result:
Timer 1
timer cancelled
Timer 2
What I am expected:
Timer 1
timer cancelled
A little about my usecase, I want to create a quite complex animation and I use the nested Timer to set the specific timing of the animation.
The problem occur when the user move forward and instantinously move backward, the animation that still inside the Timer will still run 'forward' (because it's still deep inside the nested timer) even though the 'reverse' animation should be the only one that run.
That is why I am only expecting Timer 1 to be printed instead of both Timer 1 and 2 even though the Timer has been cancelled
Any feedback or input would be appreciated.
What I am expected:
Timer 1
timer cancelled
I think this will print your expected result. Just keep a reference to the second Timer. You can reuse the existing timer variable since after the first Timer fires to create the second Timer, you no longer need a reference to the first one.
import 'dart:async';
void main(List<String> args) {
Timer? timer = null;
timer = Timer(const Duration(seconds: 1), () {
print('Timer 1');
timer = Timer(const Duration(seconds: 1),(){
print('Timer 2');
});
});
Timer(Duration(milliseconds: 1500),(){
timer?.cancel();
print('timer cancelled');
});
}

setState() or markNeedsBuild() called during build. Trying to make a simple timer in flutter

I am trying to make a simple timer which run till a given time. This is how I have tried to call the timer function. It gives the error as mentioned in the title. I believe the error is there because I am calling set state method in the init state, but I really need to make this functionality that, when this widget enters the screen, a timer begins and do something when the timer ends. Any help is greatly appreciated.
late double timeRemaining;
late Timer _timer;
void startTimer(double timeRemaing) {}
#override
void initState() {
timeRemaining =
widget.startDate.difference(widget.endDate).inSeconds / 1000 - 80;
const Duration seconds = Duration(seconds: 1);
_timer = Timer.periodic(seconds, (timer) {
setState(() {
timeRemaining--;
if (timeRemaining <= 0) {
// done = true;
done = true;
timer.cancel();
}
});
});
super.initState();
}
as the title says, you're building widget during another build (when you call setState in the timer).So the solution is to wait for the widget to finish building, then start your timer, this can be done by using addPostFrameCallback, like the following:
#override
void initState() {
timeRemaining =
widget.startDate.difference(widget.endDate).inSeconds / 1000 - 80;
const Duration seconds = Duration(seconds: 1);
// this will schedule a callback for the end of this frame.
WidgetsBinding.instance.addPostFrameCallback((_) {
_timer = Timer.periodic(seconds, (timer) {
setState(() {
timeRemaining--;
if (timeRemaining <= 0) {
// done = true;
done = true;
timer.cancel();
}
});
});
});
super.initState();
}
try it and tell me if this works

yield BLoC state inside a timer's recurring callback

How can I yield a state from the recurring callback of a Timer?
I have a Timer object in my class and when user hits start, a new object of it is created, and inside it's callback I need to call yield ....
This is the code I have so far, but it's not doing anything:
if (event is CounterETimerStart) {
timer = Timer.periodic(Duration(seconds: 1), (timer) async* {
yield CounterNewSecond(++m.passedTime);
});
}
With help from #RollyPeres from github, this is one approach:
You can add an event and react to it.
if (event is CounterETimerStart) {
timer = Timer.periodic(Duration(seconds: 1), (timer) {
add(TimerTicked());
});
}
if (event is TimerTicked) {
yield CounterNewSecond(++m.passedTime);
}

How do i pause and resume quiver.async CountdownTimer in flutter

i am trying to implement Countdown Timer in flutter. I got it working but cannot implement Pause and Resume feature of the class. Below is what i have tried:
import 'package:flutter/material.dart';
import 'package:quiver/async.dart';
void startTimer() {
CountdownTimer countDownTimer = new CountdownTimer(
new Duration(seconds: _start),
new Duration(seconds: 2),
);
var sub = countDownTimer.listen(null);
sub.onData((duration) {
// Here i tried try to check a bool variable and pause the timer
if(pauseTimer == true){
sub.pause();
}
// Here i tried try to check a bool variable and resume the timer
else if(pauseTimer == false){
sub.resume();
}
setState(() {
_current = _start - duration.elapsed.inSeconds;
});
if(_current == 0){
//Do something here..
}
});
sub.onDone(() {
print("Done");
sub.cancel();
});
}
The problem however is that only Pause is working, while the Resume is not working. Please any idea how to get both Pause and Resume working from button click.
Thanks
sub.resume() is not working because after pausing the timer onData does not trigger. You can simply make a single FlatButton and implement onTap just like this.
StreamSubscription sub; // declare it as a class variable and then assign it in function
bool isPaused =false; /// your state class variable
onTap :(){
if(isPaused)
sub?.resume(); // performing a null check operation here...
else
sub?.pause(); // ...if sub is null then it won't call any functions
}
//// Note:- make sure to cancel sub like
sub?.cancel();

Is there a way I can continuously change some text in certain interval of time?

In some area, I want a list of objects to be continuously appeared replacing the previous one in a gap of 2 secs. And in that interval I wanna do some logic.
I tried Flutter.delayed but it doesn't work accordingly in a while loop.
In your initState method, use Timer.periodic(...)
#override
void initState() {
super.initState();
// this code runs after every 2 seconds.
Timer.periodic(Duration(seconds: 2), (timer) {
if (_someCondition) {
timer.cancel(); // if you want to stop this loop use cancel
}
setState(() {
_string = "new value"; // your logic here
});
});
}
Create a timer and put your logic in the function that handles the timer event.
...
...
initstate() {
Timer.periodic( Duration(seconds: 2), (Timer t) {
setState(() => displayTheNextElementOfTheList());
...
//your logic here
});
...
}