Accessing and cancelling a periodic timer in dart/flutter - flutter

I am using a periodic timer for fetching data from server. I need to conditionally stop the timer when a bool (userExpectingTimerBool) has been set to false and also explicitly stop it by user. After stop, neither the periodic timer _timer, nor the last scheduled callback timer _t (or t) should run.
I have a fully working code now. But I feel some of it is redundant and some aspects of periodic timer is still not clear to me after reading documentation. Code is included below.
I have four questions:
Do I need to stop both _timer and _t (or t) to stop all scheduled tasks?
Which timer should I use to display search count (t.tick/_t.tick/_timer.tick)?
The callback function is a transient (nameless) method. Is the timer passed as an argument to it (t) a persistent or transient object (i.e., its value will be preserved across runs)?
What parts of the below code are redundant/can be reduced?
periodic timer with conditional run:
Timer _timer;
Timer _t;
bool userExpectingTimerBool=false;
var timerSearchCount;
var tSearchCount;
_timer = Timer.periodic(
Duration(seconds: intervalGiven),
(Timer t) {
_t= t;
if (userExpectingTimerBool == false) t.cancel();
tSearchCount=t.tick;
_displayResult();
}
);
timerSearchCount=_timer?.tick;
user-controlled stop timer function:
void stoptimer(){
setuserExpectingTimerBool(false);
if (_t !=null){_t!.cancel();}
if (_timer !=null){_timer!.cancel();}
checktimerActive();
}

When Timer.periodic invokes its callback, it should invoke it with itself as the callback's Timer argument. Therefore _t is just an alias for _timer, and having both is pointless. (You can verify this for yourself by printing identityHashCode(timer) and comparing against identityHashCode(_timer).)
See #1. Does not matter.
See #1. There is a single Timer object created by Timer.periodic.
if (userExpectingTimerBool == false)
If userExpectingTimerBool is not nullable, then it would be clearer as if (!userExpectingTimerBool). (If you believe that == false is more readable, then why not if ((userExpectingTimerBool == false) == true) and if (((userExpectingTimerBool == false) == true) == true), etc.) userExpectingTimerBool seems unnecessary anyway; if stoptimer is called, the Timer will be cancelled and prevent additional callbacks. You therefore shouldn't need to perform an additional cancellation check within the callback.
if (_timer !=null){_timer!.cancel();}
As written, _timer is non-nullable, so the null check is unnecessary. If you intend for _timer to be nullable, then you can replace the explicit if check with just _timer?.cancel();.

Related

does the Timer class effect that mush of app performance?

i have the following timer which repeat it's self every single second
Timer? myTimer ;
String myString = '';
void myChick(){
myTimer = Timer.periodic(const Duration(seconds: 1), (_) {
if(myString.isNotEmpty ){
myString = '';
// here i am doing very simple work like change some of variblse
}
});
}
Note: i am not close myTimer never and it keep repeat its self as long as user use my app
now my question does it effect too mush of my app performance ?
Use a profiler and find out determiniscaly for your use case how does tax your application, but it will depend more on your overall architecture than on the timer itself.
flutter run --profile
Watch any video online if you need to learn how does it work:

Flutter timer doesn't work when changed from milliseconds to seconds

When I change the Duration from milliseconds to seconds the app doesn't load correctly. I get no error in the debug, no logs, nothing, but the app simply doesn't respond to user interaction anymore, it's totally delayed. With milliseconds everything is normal again.
Timer.periodic(Duration(seconds: thisMaxSeconds), (timer) {
code...
}
May I know the value passed in the second (thisMaxSeconds) parameter of the Duration? If it's too high then periodic code execution will be delayed.
The likely cause here is that the thisMaxSeconds variable is set to 0. This causes the loop to run as quickly as possible, blocking UI interaction and updates.
Paste the following into dartpad to play around.
import 'dart:async';
void main() {
Timer.periodic(Duration(seconds: 0), (timer) {
print('Tick: ${timer.tick}');
if(timer.tick == 5){
print('Done');
timer.cancel();
}
});
}

Flutter - How to stop a periodic timer from outside the timer?

I have a flutter app in which I am simply opening a webview and am receiving messages via the javascript channel from the website. Now based on messages received from the website I have to start a periodic timer and stop (cancel) the timer on another message as well. As far as I can research I am just being allowed to cancel from within the timer or from dispose if the timer was initialized in initstate.
Here is my code right now -
JavascriptChannel _lightVibrationJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'x',
onMessageReceived: (JavascriptMessage message) {
var pattern = json.decode(message.message);
if (pattern["eventName"] == "start") {
//Make Periodic Timer
} else if (pattern["eventName"] == "stop") {
//Stop Periodic Timer
}
});
}
If I am making a global timer and initializing it at the time of making the periodic timer it will obviously not allow me to cancel it from within the else condition. I am bit lost. Any help?

Is there a way for Flutter's Timer.periodic to wait for a function return plus a fixed duration before moving to the next cycle?

I use Timer.periodic with a duration normally. This works perfectly for my current use case.
Duration fiveSecs = const Duration(seconds: 5);
new Timer.periodic(fiveSecs, checkChange);
void checkChange(Timer timer) async {
//do some network calls
}
In this particular case I make network calls that take no longer than 500ms, and doing them every 5 seconds is enough for whatever depends on what those calls return.
Now I want to check for new values as frequently as 2 seconds, however, based on what needs to be checked a network call could take anywhere from a few hundred milliseconds to even 10 seconds. I could give a large margin and use a frequency of like 20 seconds for the timer, but in this case I would even prefer a 1 second frequency if possible.
So is there a way the timer could wait for a function, and still have a fixed duration like 1 second. So the maximum time it would take would be 1 second + callback runtime? Or is there a better way this could be achieved, I'm open to suggestions. Thank you.
void startTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) async {
print("lets wait for 5 seconds");
_timer.cancel();
await Future.delayed(Duration(seconds: 5));
print("Job is done");
print(DateTime.now());
print("Do it again");
startTimer();
});
}
I have encountered the same situation lately,
what I did was (in my case inside a static class) to add a static boolean value and toggle it accoding to my situation needs, later checking it's value inside the Timer.Periodic callback.
Timer.periodic(ConstantValues.socketTimerOperationDelay, (Timer t) async{
if(_udpHandlerCompleted){
_udpHandlerCompleted = false;
if(!_shouldSendBroadcast || _shutDown)
t.cancel();
else
_writeAllToUdpSocket(_udpSocket, data, ConstantValues.udpMulticastGroup, ConstantValues.udpMulticastPort);
_udpHandlerCompleted = true;
}
});
As you can see in my situation, I had to wait for the callback to end before I move to the next one, I believe this is what you're looking for, but in case you need to await for a different method the solution would be similar,
simply toggle the boolean _udpHandlerCompleted (in my case) in the other function.
Edit:
If my soultion helped you, kindly mark it as the accepted answer. Good luck!
At the end checkChange add Future.delayed(const Duration(seconds: 5), checkChange) then call it once instead of running the timer. You can add/check a boolean flag if you need to kill it at any point.

Timer not firing every second on WatchKit

This timer isn't firing every second, when I check the log and UI it seems to be firing every 3-4 seconds.
func startTimer() {
print("start timer")
timer = Timer.scheduledTimer(timeInterval: 1,
target: self,
selector: #selector(timerDidFire),
userInfo: nil,
repeats: true)
}
func timerDidFire(timer: Timer) {
print("timer")
updateLabels()
}
Is this just something that is going to happen on the Watch due to lack of capabilities, or is there something wrong in my code?
Here is the log if needed:
0.0396000146865845
3.99404102563858
7.97501903772354
11.9065310359001
EDIT:
And for clarification, what I'm updating every second is the workout timer, so it needs to be updated every second that ticks by.
If your app is busy doing something else, which blocks or delays the run loop from checking that the fire time has repeatedly passed, the timer will only fire once during that period:
A repeating timer always schedules itself based on the scheduled firing time, as opposed to the actual firing time. For example, if a timer is scheduled to fire at a particular time and every 5 seconds after that, the scheduled firing time will always fall on the original 5 second time intervals, even if the actual firing time gets delayed. If the firing time is delayed so far that it passes one or more of the scheduled firing times, the timer is fired only once for that time period; the timer is then rescheduled, after firing, for the next scheduled firing time in the future.
As an aside, it may be more efficient to update your UI based on a response to a change (e.g., observation), or reaction to an event (e.g., completion handler).
This avoids creating busy work for the app when it's driven to check yet doesn't actually have a UI update to perform, if nothing has changed during the timer interval.
It also prevents multiple changes within the fire interval from being ignored, since a timer-driven pattern would only be displaying the last change in the UI.
Consider using a WKInterfaceTimer label in place of the label that you are using to show the timing:
A WKInterfaceTimer object is a special type of label that displays a
countdown or count-up timer. Use a timer object to configure the
amount of time and the appearance of the timer text. When you start
the timer, WatchKit updates the displayed text automatically on the
user’s Apple Watch without further interactions from your extension.
Apple Docs.
WatchOS will then take responsibility for keeping this up-to-date. The OS handles the label for you, but you have to keep track of the elapsed time: you just need to set an NSDate to do that (see example below).
Sample Code.
In your WKInterfaceController subclass:
// Hook up a reference to the timer.
#IBOutlet var workoutTimer: WKInterfaceTimer!
// Keep track of the time the workout started.
var workoutStartTime: NSDate?
func startWorkout() {
// To count up use 0.0 or less, otherwise the timer counts down.
workoutTimer.setDate(NSDate(timeIntervalSinceNow: 0.0))
workoutTimer.start()
self.workoutStartTime = NSDate()
}
func stopWorkout() {
workoutTimer.stop()
}
func workoutSecondsElapsed() -> NSTimeInterval? {
// If the timer hasn't been started then return nil
guard let startTime = self.workoutStartTime else {
return nil
}
// Time intervals from past dates are negative, so
// multiply by -1 to get the elapsed time.
return -1.0 * self.startTime.timeIntervalSinceNow
}
Comprehensive blog entry: here.
As of 2021, the (Foundation) Timer object supports a tolerance variable (measured in seconds). Set timer.tolerance = 0.2, and you should get a fire every second (+/- 0.2 seconds). If you are just updating your GUI, the exact time interval isn't that critical, but this should be more reliable than using no tolerance value. You'll need to create the timer separately, and manually add to the run queue such as below... (Swift)
import Foundation
// Set up timer to fire every second
let newTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {timer in
self.timerFired()
}
newTimer.tolerance = 0.2 // For visual updates, 0.2 is close enough
RunLoop.current.add(newTimer, forMode: .common)