delay after setState is triggered - flutter

in the code below that is for throwing dice, I want to wait 2 seconds after each dice throwing. I tested sleep(duration) and await Future.delayed(duration); the first one makes a delay before updating the screen which means when I tap the TextButton, it waits for 2 seconds and then changes the screen, but I want it to be changed and then waits for 2 seconds. The second one actually does nothing and there is no delays.
Here is the code:
Duration delay = const Duration(seconds: 2);

You can call delay inside WidgetsBinding, like this:
setState(() {
...
});
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(Duration(seconds: 2));
print("call"); // update the view then print call after 2 second
});

try..
//Move your onPressCode to a function
Future<void> rollDice() async{
await Future.delayed(Duration(milliseconds: 0), () {
/// your onPressCode
});
}
And on your onPress :
onPressed:() async {
await rollDice();
await Future.delayed(Duration(milliseconds: 2000), () {});
print('this print is show after 2 secs');
}
Edit ...
For prevent double tap, you can create a state called canPress with default value = false ...
onPressed:() async {
if (canPress){
setState(() => canPress = false);
await rollDice();
await Future.delayed(Duration(milliseconds: 2000), () {});
print('this print is show after 2 secs');
setState(() => canPress = true);
}
}

Related

How to automatically ontap in flutter

Please help i have a video recording app which starts with a tap and a timer is also started with the same tap and after another tap it stops and timer reset . I want to stop and reset timer when the timer reaches 15 second how to do this please help
GestureDetector(
onTap:
() async {
if (isRecoring) {
stop();
XFile videopath =
await _cameraController.stopVideoRecording();
setState(() {
isRecoring = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => VideoViewPage(
path: videopath.path,
)));
}
);
} else {
startTime();
await _cameraController.startVideoRecording();
setState(() {
isRecoring = true;
// ignore: use_build_context_synchronously
});
}
},`
i tried adding logical or like this ` if (isRecoring || timer == '15') {stop} but didnt work.
create a diffrent function with a if condition for stop even that didnt work
Firstly you need to define a Duration variable and a Timer to track the timer duration:
var videoDuration = const Duration();
Timer? autoStopRecordingTimer;
Then make a function which will stop the video recording:
void stopRecording()async{
stop();
XFile videopath = await _cameraController.stopVideoRecording();
setState(() {
isRecoring = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => VideoViewPage(
path: videopath.path,
)));
}
);
}
When starting video recording, start the timer as well. Which will stop the recording if it has been 15 or more seconds:
autoStopRecordingTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
if(videoDuration.inSeconds>=15){
autoStopRecordingTimer?.cancel();
videoDuration = const Duration();
stopRecording();
} else {
videoDuration+=const Duration(seconds: 1);
}
});

create async await steps with Timer instead of Future.delayed(...)

Is it possible to perform async steps with Timer instead of Future.delayed? The main reason is the availability of cancel() method in the Timer class, which allows me to cancel a running timer when a widget is disposed in Flutter.
import 'dart:async';
const kFirstStepDuration = Duration(seconds: 1);
const kSecondStepDuration = Duration(seconds: 1);
const kThirdStepDuration = Duration(seconds: 1);
void main() async{
await Future.delayed(kFirstStepDuration);
print('first step');
await Future.delayed(kSecondStepDuration);
print('second step');
await Future.delayed(kThirdStepDuration);
print('end');
}
Using the Timer following Richard Heap answer, I would like to cancel the steps whenever its possible, but the code below prompts:
first timer to begin
cancel first
cancel second
cancel third
My expectation was to prompt:
first timer to begin
cancel first
second timer to begin
cancel second
third timer to begin
cancel third
executed in 0:00:00.06000
import 'dart:async';
const kFirstStepDuration = Duration(seconds: 1);
const kSecondStepDuration = Duration(seconds: 1);
const kThirdStepDuration = Duration(seconds: 1);
Timer? firstTimer;
Timer? secondTimer;
Timer? thirdTimer;
final firstCompleter = Completer();
final secondCompleter = Completer();
final thirdCompleter = Completer();
void main() {
threadA();
threadB();
}
void threadA() async {
print('first timer to begin');
firstTimer = Timer(kFirstStepDuration, ()=> firstCompleter.complete());
await firstCompleter.future;
print('second timer to begin');
secondTimer = Timer(kSecondStepDuration, ()=> secondCompleter.complete());
await secondCompleter.future;
print('third timer to begin');
thirdTimer = Timer(kThirdStepDuration, ()=> thirdCompleter.complete());
await thirdCompleter.future;
}
void threadB() async {
await Future.delayed(Duration(milliseconds: 20));
firstTimer?.cancel();
print('cancel first');
await Future.delayed(Duration(milliseconds: 20));
secondTimer?.cancel();
print('cancel second');
await Future.delayed(Duration(milliseconds: 20));
thirdTimer?.cancel();
print('cancel third');
}
await a future that you complete in the timer's callback.
print('starting');
final completer = Completer();
final t = Timer(Duration(seconds: 3), () => completer.complete());
await completer.future;
print('done');
The problem with this is the completer won't complete if the timer is cancelled, as the callback is never called. So you have to cancel the timer and complete the completer.
Wouldn't it be easier to check the widget is still mounted after the Future.delayed?
EDIT
Following the update to your question, it seems you didn't the 'problem' above. You could encapsulate all the functionality into a class like:
class PointlessTimer {
PointlessTimer(Duration d) {
_timer = Timer(d, () => _completer.complete());
}
late final Timer _timer;
final _completer = Completer();
Future get future => _completer.future;
void cancel() {
_timer.cancel();
_completer.complete();
}
}
and use it like this:
void main() async {
print('starting');
final pointless = PointlessTimer(Duration(seconds: 3));
Timer(Duration(seconds: 2), () => pointless.cancel());
await pointless.future;
print('done');
}
Following Richard Heap answer:
/// works as a Future.delayed. Returns a timer to be canceled.
/// useful when disposing widgets before a timer completion.
/// necessary when testing widgets that disposes before the timer completion.
Future<Timer> _delayed(Duration duration) async {
final completer = Completer();
final timer = Timer(duration, () {
completer.complete();
});
await completer.future;
return timer;
}

Semaphore in flutter

Code:
void _test() {
print(1);
Timer.run(() {
print(2);
});
print(3);
}
Print 1 3 2.
I want print 1 2 3.
In iOS I can use Semaphore, how can I do this in flutter?
Awaiting for a method to complete can be written inside a future
void _test() async{
print(1);
Future.delayed(Duration(seconds : 0), (){
print(2);
});
print(3);
}
//Output 1 3 2
void _test() async{
print(1);
await Future.delayed(Duration(seconds : 0), (){
print(2);
});
print(3);
}
//Output 1 2 3
In the second example the await will wait for the future to complete and then move to the next task
Thanks #jamesdlin, I solved with his comment, below is desired code:
void _test() async {
print(1);
Completer<void> completer = Completer();
Timer.run(() {
print(2);
completer.complete();
});
await completer.future;
print(3);
}
Timer.run() basically tells the program that here is a piece of code that should be executed but I don't care if it finishes before it reaches the code outside of this block.
If you want to wait a bit, use Future.delayed:
runAsyncMethod() async {
print("1");
await Future.delayed(Duration(seconds: 3), () {print("2");});
print("3");
}

Flutter Future.delayed timer dispose when navigating to other page

In my flutter app, I have a function that has delaye for 5 seconds to activate a button. When I navigate to other page, the timer of the delayed is still working even though I use the "pushReplacement" navigator. Can anyone help me find a way to dispose or cancel this timer when I navigate to other page.
here is the code:
Future sendVerificationEmail() async {
try{
final user =FirebaseAuth.instance.currentUser!;
await user.sendEmailVerification();
setState(() => canResendEmail = false);
await Future.delayed(const Duration(seconds: 5)); // this is the line causing the error
setState(() => canResendEmail = true);
}catch (e) {
Utils.showSnackBar(e.toString());
}
}
and here is the navigation button function:
Future<void> SignOut() async {
await FirebaseAuth.instance.signOut();
Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => mainPage()),
);
}
Try using a timer instead
Timer timer = Timer(Duration(seconds: 5), () {
//do something here();
});
// You can dispose the timer like this
timer.cancel();

Alert Dialog running infinitely

Hello I am trying to run following code, I want to run a specific asynchronous code and show alert dialog until it's running. But the code is not being executed after await showAlertDialog(); this line.
void appendAndRunPythonCode() async {
await showAlertDialog();
await runPythonScript(final_code);
_alertDialogUtils.dismissAlertDialog(context);
}
This is how my showAlertDialog() function is implemented:
Future<void> showAlertDialog() async {
if (!_alertDialogUtils.isShowing) {
await _alertDialogUtils.showAlertDialog(context);
}
}
runPythonCode():
Future<void> runPythonScript(String code) async {
if (inputImg == null) {
ToastUtils.showToastMessage(text: ConstUtils.input_image_empty_notice);
return;
}
if (code.isEmpty) {
ToastUtils.showToastMessage(text: ConstUtils.code_empty);
return;
}
List<String> lines = code.split('\n');
String lastLine = lines.elementAt(lines.length - 4);
if (lastLine.split(' ').elementAt(0).compareTo('outputImage') != 0) {
ToastUtils.showToastMessage(text: ConstUtils.cv_error_line2);
return;
}
data.putIfAbsent("code", () => code);
data.putIfAbsent("inputImg", () => inputImg);
_alertDialogUtils.showAlertDialog(context);
final result = await _channel.invokeMethod("runPythonCVScript", data);
// Add Artifical Delay of 3 seconds..
await Future.delayed(
Duration(seconds: 3),
);
_alertDialogUtils.dismissAlertDialog(context);
setState(
() {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
output = result['textOutput'] ??= "";
error = result['error'] ??= "";
outputImg = (result['graphOutput']);
data.clear();
},
);
}
You shouldn't await the showAlertDialog because runPythonScript won't be executed until the dialog is dismissed.
Remove the await.
Like so:
void appendAndRunPythonCode() async {
showAlertDialog();
await runPythonScript(final_code);
_alertDialogUtils.dismissAlertDialog(context);
}