In flutter, how to trigger something strictly after an async function is done executing? - flutter

Say I have a _mode property that I want to change in setState() when a button is pressed.
When the button is pressed (onPressed), I am calling this function,
Future <void> changeMode() async {
_result = await getResult(); // Returns a result. I want the mode to change AFTER the result is returned
setState((){
_mode = Modes.OFF;
});
}
What ends up happening is, the _mode is changed before the getResult() is done executing. How should I go fixing this?

When you use await inside a function, that function is in danger of blocking the main thread, so it must be marked as async.
Mark your function with async and it should work. Your state setting function also should be a parameter of setState().
Future<void> changeMode() async {
_result = await getResult(); // Returns a result. I want the mode to change AFTER the result is returned
setState(() {
_mode = Modes.OFF;
});
}

Related

Run multiple asyn function flutter one after one flutter

hello I want have to run two functions(Function1() and Function2()) and store value of these returns and run third function. But some time according to condition Function1() or Function2() or both not be run.
if(condition1){
await Function1();
}
if(condition2){
await Function2();
}
await Functon3();
I try as above but Function3() run simultaneously with Function1() or with Function2().
My Function1() code looks like following...
Future Function1() async {
apiService
.apiFileUpload()
.then((value) async {
///codes goes here
}).catchError((error) {
print('EEEE: ' + error.toString());
});
}
If anything not clear please let me know in the comment section.
Please do not use .then() in combination with async/await. It is technically possible, but it takes some skill to get it right, so why make it hard on yourself. Stick with one way of doing it, use either one or the other. You mixed it up and through a slight oversight, your Function1 does not actually wait on it's result. It just returns, with the function still running in the then block. So you await it, but that does not help.
Since you are using await already, stick with that and remove .then() from your repertoire for now:
Future Function1() async {
try {
final value = await apiService.apiFileUpload();
///codes goes here
} catch(error) {
print('EEEE: ' + error.toString());
}
}
You can use await
Future Function1() async {
try{
final value = await apiService
.apiFileUpload();
final value2 = await secondFuntion();
///add more and condition on values
} catch(e){
.....
}
}
from your question you need to tell the compiler to stop on particular task with await and avoid using then function it will never stop your compiler
your future fuction:
Future Function1() async {
apiService
.apiFileUpload()
.then((value) async {
///codes goes here
}).catchError((error) {
print('EEEE: ' + error.toString());
});
}
Modified Future func
Future Function1() async {
var result = await apiService.apiFileUpload();
if(result == success){
// code goes here
}else{
//you can show your error here
}
}

Flutter Bloc Error : emit was called after an event handler completed normally - between two functions

I have the following problem...
emit was called after an event handler completed normally. This is
usually due to an unawaited future in an event handler. Please make
sure to await all asynchronous operations with event handlers and use
emit.isDone after asynchronous operations before calling emit() to
ensure the event handler has not completed.
BAD on((event, emit) {
future.whenComplete(() => emit(...)); });
GOOD on((event, emit) async {
await future.whenComplete(() => emit(...)); }); )
What happens is that in a function called _onLogIn, if the user has changed the language, it goes from there to another function inside the bloc, these two functions do not depend on each other, I mean that each function is called in different pages of the application, but still _onLogIn checks the _onChangeLanguage function.
UserBloc({this.usecases}) : super(UserInitial()) {
on<LogInEvent>(_onLogIn);
on<ChangeLanguageEvent>(_onChangeLanguage);
}
_onLogIn function :
void _onLogIn(
LogInEvent event,
Emitter<StateA> emit,
) async {
emit(UserLoading());
final userOrFailure = await services.logIn(
x: event.x,
y: event.y,
);
await userOrFailure.fold((user) async {
/// If the user is logging in for the first time and does not
/// have a preferred language.
if (user.preferredLanguage == null) {
emit(UserSuccess());
emit(UserAlreadyLogged(connectedUser));
} else {
/// An ChangeLanguageEvent object
ChangeLanguageEvent event = ChangeLanguageEvent(
user.preferredLanguage,
user.someId,
);
/// Call the other function in the same bloc
this._onChangeLanguage(
event,
emit,
isFromLogin: true,
);
}
}, (failure) {
emit(UserError(failure.message));
});
}
_onChangeLanguage function :
void _onChangeLanguage(
ChangeLanguageEvent event,
Emitter<StateA> emit, {
bool isFromLogin = false,
}) async {
final successOrFailure = await services.updateLanguage(
event.language,
event.someId,
);
await successOrFailure.fold( // ! HERE THE ERROR WHEN I LOG IN; but when i changed the language from the application i don't have an error
(language) async {
emit(ChangeAppLanguage(language));
final sessionOrFailure = await services.getSession();
sessionOrFailure.fold(
(session) {
/// I need this condition to know if the language comes from login
if (isFromLogin) {
emit(UserSuccess());
}
emit(UserAlreadyLogged(session));
},
(failure) => emit(UserError(failure.message)),
);
},
(failure) {
emit(UserError(failure.message));
},
);
}
Any idea why? Thank you
void _onChangeLanguage(
ChangeLanguageEvent event,
Emitter<StateA> emit, {
bool isFromLogin = false,
}) async
This should be a major red flag. A call marked as async, but not returning a Future<>. There is no way, the caller could possibly await this call. Or even know that they should await this call.
Make it return a proper Future<void> instead of just void and your bloc should pick up on that and properly await the call.
There even is a linter rule for this: avoid_void_async. Did you turn off your linter? Don't do that. Turn your linter on and listen to it. Your other function has the same problem.
In my case I had to return Future<...>, but have not done it in the 'switch' statement, rather I've used the 'break' on all cases. So a compiler have not pointed me the lack of the 'return' statement.
After I have put 'return' on all cases, the error disappeared.

how do i force Flutter to run all the lines inside the function?

I have a this function that I need to run during initstate() in its entirety before building the widget. I have used this kind of code before in some parts of my app and it works there, but in this case, flutter jumps out before executing .then , goes to build the widget tree, and then returns back to the function but skips the remaining lines. I'm confused where I should properly put async-awaits to force it to finish the block. Also, can I ask where I can read an explanation of the proper flow of execution for flutter so that I can understand it more?
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get()
.then((value) async {
return await value.exists ? _result = true : _result = false;
}
);
return _result;
await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.
you can write your code like this-
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get();
vendorDocRef.exists ? _result = true : _result = false;
return _result;
}

Call for a definite value from an async method in flutter. waiting problem

How to write a function which fetches value from an async method ?
Future FetchValuefromService(String tk) async{ await .. //Fetch from api return value; }
void mainfunction(){ double newval =FetchValuefromService('hello'); //.... do something with newval. }
if i make mainfunction() async then the "do something" in mainfucntion will not get the double value. I need to get newval from service and then only i need to proceed.
Making a function async ables to use the await keyword in its body. Place it to the left of a Future<T> expression to pause the execution until the Future completes and the T value is obtained.
In your case, it would be double newval = await FetchValuefromService('hello');

Return code results after some delay in Flutter?

I'm trying to run this Future function that runs a Timer once 500 milliseconds have passed. The issue I'm having is that the timer is not passing the data to the results variable so the _destinationOnSearchChanged() ends up returning null.
Note: getCity(context, _typeAheadController.text); does return data but only inside the Timer function.
Future _destinationOnSearchChanged() async {
dynamic results;
//Cancels timer if its still running
if (_apiCityThrottle?.isActive ?? false) {
_apiCityThrottle.cancel();
}
//Makes API call half a second after last typed button
_apiCityThrottle = Timer(const Duration(milliseconds: 500), () async{
results = await getCity(context, _typeAheadController.text);
});
print(results);
return await results;
}
As pskink noted in a comment, you probably should look into existing debounce mechanisms instead of creating your own.
If you still want to proceed down this path: your problem is that create a Timer and then return results immediately. You don't wait for the Timer to fire (and you can't directly await a Timer). In this case, you could use a Completer:
Future _destinationOnSearchChanged() async {
var timerCompleter = Completer<dynamic>();
// Cancels timer if it's still running.
_apiCityThrottle?.cancel();
// Makes API call half a second after last typed button.
_apiCityThrottle = Timer(const Duration(milliseconds: 500), () async {
timerCompleter.complete(await getCity(context, _typeAheadController.text));
});
var results = await timerCompleter.complete();
print(results);
return results;
}