How to start/stop inactivity timer in Flutter - 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

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.

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.

Flutter check is page on top of the screen stack

I have a page A that does some tasks for every time interval. I only want to perform these tasks only if page A is active and showing on the screen.
If the screen is showing page B, it will NOT perform the tasks.
How should I approach this problem?
It's fairly simple to do without checking the page stack. Since if it's on top of the page stack is only the case when that page is currently ACTIVe, meaning all other pages are removed (popped) from the stack. If you called for a new page with Navigator.of(context).push...., in order to 'pause' the previous page, you could await that action. The following example will a periodic timer (remember, you have to have it outside of the scope of the function, for example, in the state) and assign it to already existing Timer variable.
Timer t; //a variable in a Stateful widget
#override
void initState() {
super.initState();
//it's initialized somewhere and is already working
t = Timer.periodic(
Duration(milliseconds: 500),
(t) => print(
'CALL YOUR FUNCTION HERE ${t.tick}',
),
);
}
_someMethodInvokingPageB() async {
// Cancel the timer before the opening the second page
// no setState((){}) is needed here
t.cancel();
// Your calling of Page B. Key is the word AWAIT
// AWAIT will pause here until you are on the Page B
var result = await Navigator.of(context).pushNamed('/pageB');
// reassign a timer to your timer
// you don't need setState((){}) here
t = Timer.periodic(
Duration(milliseconds: 500),
(t) => print('CALL YOUR FUNCTION HERE ${t.tick}'),
);
}
That's how you have a timer, you have a method where you open Page B and before opening Page B you cancel that timer, await the opening of Page B and after you finish stuff on Page B, you reassign a new timer to your Timer t variable.
P.S. Don't forget to call t.cancel() in your dispose() method!

What is the difference between Future.delayed vs Timer in 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.