What is the difference between Future.delayed vs Timer in flutter - flutter

I like to know the differences between Future.delayed and Timer method for delaying code execution. Both seem to do the same thing.
Future.delayed
Future.delayed(const Duration(milliseconds: 500), () { /*code*/ });
VS
Timer
Timer _timer = new Timer(const Duration(milliseconds: 500), () { /*code*/ });

A couple of differences for me.
Future.of returns a Future.
Timer does not return anything.
So if your delayed code returns anything that you need to continue your working, Future is the way to go.
Other difference is that the Timer class provides a way to fire repeatedly.
This quote is from the Timer Class Reference documentation itself
A count-down timer that can be configured to fire once or repeatedly
And example to use Timer with the repeat could be
Timer.periodic(Duration(seconds: 5), (timer) {
print(DateTime.now());
});
Other frecuent example is to create a stopwatch, to measure timings in your code, it's usually seen using a Timer.
GL!!

Timer:
Timer() creates a Timer object, that runs your computation after a delay. Since you get the reference to that Timer object, you can choose to cancel it before it's fired, by calling cancel.
Timer t = Timer(Duration(seconds: 1), () => print("1 sec later"));
t.cancel(); // nothing will be printed out
Future:
Future.delayed creates a Future that runs its computation after a delay. Internally, it's still using a Timer to do that. It does not expose the timer to you, so you cannot control or cancel it. On the bright side, you get to do your normal Future stuff, like await on it.
await Future.delayed(Duration(seconds: 1);
print("1 sec later");

Use Timer if:
You want the ability to cancel it. With Timer.cancel() you cancel the timer unlike Future where you'll have to make use of CancelableCompleter to cancel the Future.
If you don't want to return anything in your callback method.
Example:
// Prints 'Hello' after 1s.
var timer = Timer(Duration(seconds: 1), () => print('Hello'));
And in case you decide to cancel it, use:
timer.cancel();
Use Future if:
Your code can throw errors and you want to catch them. Had you used Timer and any uncaught exceptions occurs, the app will exit.
You want to return something from your callback method.
Example:
// Return 'Hello' after 1s and if there is any error, it will be caught.
Future
.delayed(Duration(seconds: 1), () => 'Hello')
.catchError((err) {});

The timer runs its job after the given duration, but flutter not waiting for it to complete its execution, it performs below statements.
Example:
Timer(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
print("Other code");
Output:
Other code
Execute this code after 2 seconds
So as you can see code below timer will execute first and then the timer will be performed.
Also, Timer can be stopped at any given point before its execution, if we crate the object of it.
Timer timer = Timer(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
timer.cancel();
The future also runs its job after the given duration, but its return future object means we can use await to get its execution first, and then below statements will be going to execute.
await Future.delayed(Duration(seconds: 2), () {
print("Execute this code afer 2 seconds");
});
print("My Code");
print("Other code");
Output:
Execute this code after 2 seconds
Other code
The main disadvantage of the future is that we can't cancel it in between.

Related

Firestore requests not working properly in Time.periodic

I want to write firestore database every 15 seconds. This is the code I have right now:
void startTimer() {
const oneSec = const Duration(seconds: 15);
_timer = new Timer.periodic(
oneSec,
(Timer timer) async {
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
})
},
);
}
Then I call startTimer() in my build function. It writes data to the database, but the performance is weird. Instead of writing the database every 15 seconds, it does it almost every second, and then the number of writes is unpredictable (more than one), which then leads to a memory leak. I don't know what and where to troubleshoot, because I don't see any problem in my code
How can I make it write exactly 1 entry to the database every 15 seconds?
I can't say for sure, but from the context you've given in your question I think I have a good idea of what's going on.
you're calling the timer from your build function
you used to have const oneSec = const Duration(seconds: 1); - either that or your variable naming needs some work
you're never cancelling the timer
So there's a few things going on here. The most obvious issue is that the widget's build function can and will run more than once in almost all applications. You should never rely on it not running more than once - that's not what it is meant for.
Because you initially started your widget with a timer running once a second, that will continue to run indefinitely. If you then do multiple build updates, that would then create more timers which also run indefinitely - which is exactly why you were seeing the number of writes grow as well as the unexpected timing.
Instead, you should be making your widget into a StatefulWidget and initiating the timer in an overridden initState() method (which will only run once per widget but should not access the context, which is fine in your case).
This would look like this:
class MyWidgetState extends State<MyWidget> {
late final Timer _timer;
#override
initState() {
super.initState();
startTimer();
}
#override
dispose() {
stopTimer();
super.dispose();
}
void startTimer() {
const oneSec = const Duration(seconds: 15);
_timer = new Timer.periodic(
oneSec,
(Timer timer) async {
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
})
},
);
}
void stopTimer() {
_timer.cancel();
}
}
If you did need to access the context, you'd instead need to use an overridden didChangeDependencies() method which can run more than once but likely won't unless you're using inherited widgets. In this case you would need to put in some protections to make sure the timer wasn't created more than once i.e. make it nullable and check if it is set before creating a new one.
Or if you really do need to call the startTimer() function from your build function, you could do it the same way as mentioned above - make _timer nullable and check if it is set before making a new one. But I'd recommend avoiding this - if you always keep things that are started in initState and then always stop them in dispose, it will be a lot easier to figure out what's going on in the class.
I would say that using an asynchronous method inside a Timer could be the problem, However since you want to execute your method every 15 seconds, think about making a recursion method, with Future.dalayed like this:
int updateCount = 0; // this will allow us to track how many times the method is executed
recursionTimerUpdate() async {
await Future.delayed(Duration(seconds: 15)); //
updateCount += 1;
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
});
bool shouldContinueWorking = updateCount < 10;
if (shouldContinueWorking) {
await recursionTimerUpdate();
}
}
this is a recursion method, that will execute your update() method every 15 seconds, and will stop from continuing after 10 times it's run, If you want it to not stop in your app, just set shouldContinueWorking = true.

How to use Timer and await together in Dart/Flutter?

I have a code like below:
Timer(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
});
print("This is printed when Timer ends");
How can I use "await" in this case? I want when Timer ends then run next code below Timer. I used Future.delayed(), it can do it but I can't skip the delay time in Future.delayed() like in Timer(). Because i want to skip the delay time if the condition is true. Because I want to skip the delay time if the condition is true. If I use Future.delayed(), it doesn't have cancel() method like Timer().
Please tell me the solution. Thanks
Try Future.delayed instead of Timer
await Future.delayed(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
});
print('This is printed when Timer ends');
Timer(Duration(seconds: 5),(){
print("This is printed after 5 seconds.");
printWhenTimerEnds();
});
void printWhenTimerEnds(){
print("This is printed when Timer ends");
}
When you wish to skip the timer just call timer cancel and the printWhenTimerEnds() method
There are several ways to achieve what you need. You can use Completer and Future.any like this:
import 'dart:async';
Completer<void> cancelable = Completer<void>();
// auxiliary function for convenience and better code reading
void cancel() => cancelable.complete();
Future.any(<Future<dynamic>>[
cancelable.future,
Future.delayed(Duration(seconds: 5)),
]).then((_) {
if (cancelable.isCompleted) {
print('This is print when timer has been canceled.');
} else {
print('This is printed after 5 seconds.');
}
});
// line to test cancel, comment to test timer completion
Future.delayed(Duration(seconds: 1)).then((_) => cancel());
Basically we are creating two futures one is a delayed and another one is cancelable future. And we are waiting for the first one that completes using Future.any.
The other option is to use CancelableOperation or CancelableCompleter.
For example:
import 'dart:async';
import 'package:async/async.dart';
Future.delayed(Duration(seconds: 1)).then((_) => cancel());
var cancelableDelay = CancelableOperation.fromFuture(
Future.delayed(Duration(seconds: 5)),
onCancel: () => print('This is print when timer has been canceled.'),
);
// line to test cancel, comment to test timer completion
Future.delayed(Duration(seconds: 1)).then((_) => cancelableDelay.cancel());
cancelableDelay.value.whenComplete(() {
print('This is printed after 5 seconds.');
});
Here we do practically the same thing as above but with already available classes. We are wrapping Future.delayed into CancelableOperation so we are able now to cancel that operation (Future.delayed in our case).
Another way you can wrap Timer into future with Completer and so on.

Timer not cancelling flutter

I needed a single instance of a timer to run on my app such that i can cancel and reinitialise it when needed. However, the timer doesn't cancel after calling the .cancel() operation. It only work if i call it from the default constructor but i want a global timer i can cancel anytime;
Timer timer;
timer = Timer.periodic(Duration(seconds: 10), (Timer t) async {
//cancelling timer only works here e.g (t.cancel)
print("loop operation");
});
timer.cancel(); // calling this method outside the constructor don't work.
I had a similar problem. This happened after I made the callback function async.
I think the solution will differ from problem to problem depending on why your callback is async.
This is not a solution, but rather a hint to get you closer to the solution.
Timer timer;
Timer.periodic(Duration(seconds: 10), (Timer t) async {
timer = t;
print("loop operation");
});
//inside a button callback
timer??.cancel(); // perform null check as well before cancelling.

What is best way to call a function if there no response from showDialog after x seconds in Futter

I'm kinda new to flutter. I searched but couldn't find a suitable answer to this... What would be the best way to call a function if the user doesn't respond to a showDialog alert after x seconds? If the user presses a button, then I don't want the function to execute.
You can start a Timer for x seconds as soon as you display your dialog & then execute your function. If the user clicks your button, you can stop your timer.
Timer _timer;
bool userResponded = false;
You will need a StatefulWidget & wherever you show your dialog, you need to start the timer.
showDialog(...); // Your showDialog method
// You have to update userResponded to true if user clicks on your dialog or whatever
// It should look something like this: setState(() => userResponded = true);
_timer = Timer(const Duration(seconds: 10), () { // Start your timer for x seconds
if (!userResponded) { // If user didn't respond
// execute your function
}
});
Also, you need to override the onDispose method & stop the timer:
#override
void onDispose() {
_timer?.cancel();
super.onDispose();
}
For this, you would use a Timer widget.
Timer example:
Timer _timer = Timer(
const Duration(milliseconds: 500),
() {
// Call some function after delay of 500ms
},
);
To cancel the timer, use _timer.cancel();
So, most likely in the initState method you'll want to set timer object, then when the user presses the button, you can cancel this timer which means the callback in it won't run after a delay you've specified.

How to start/stop inactivity timer in Flutter

Is there a way to start an inactivity timer for 5 seconds or X seconds WHILST a function executes?
Here is what I'm doing:
I'm creating a screenshot:
//todo: I need an activity timer [start] here
screenshotController.capture(
//delay: Duration(seconds: 5),
pixelRatio: 2,
path: newPath
).then((io.File image) {
//Capture Done
_imageFile = image;
}).catchError((onError) {
print(onError);
});
// todo: I need an activity timer [stop] here.
The user cannot interrupt the creation of the PNG file. I need some kind of progress timer to start/stop. I do not wish to use Progress_HUD. This is very ugly. I've tried it. I have to change my entire code to accommodate how this app works.
I'm inclined to use CircularProgress..().. but how can I make it start? and how can I make it stop??
How can I know when the screenshotController is still active?
I don't know a lot about screenshot plugin but if you just want to run a function every x seconds you can use Timer
In initState()
void initState() {
super.initState();
Timer.periodic(Duration(seconds: 1), (_) {
//Write here your function and logic
});
}
if your function is async put the timer in didChangeDependencies(),
you can stop the timer by writing some logic