Asynchronous method not running in proper order - flutter

I have these methods, for some reason fetchItems is being called first before initPosition, how come dart wont wait for it to finish and proceeds to the second method? I've added async/await but it still doesn't work. I've also checked my backend logs to confirm this. Am I doing something wrong?
Future<void> initPosition() async {
if (_latitude != null && _longitude != null) {
await Socket.updatePosition(
lat: 51,
lon: 17,);
}
}
Future<void> initMarkers() async {
await initPosition();
await Provider.of<Items>(context, listen: false)
.fetchItems();
}
void initMapState() async {
await getCurrentLocation().then((_) async {
await initMarkers();
setState(() {
_loaded = true;
});
});
}
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) {
initMapState();
});
}
Future<void> fetchItems() async {
itemList = await repository.getItemList();
notifyListeners();
}

Working with multiple asynchronous functions inside Futures depends on whether one is finished or not, not every single one. For this, you can call the "whenComplete" method so you can assure that your future function have finished running. Like this:
For your initMarkers() function:
Future<void> initMarkers() async {
await initPosition().whenComplete((){
Provider.of<Items>(context, listen: false)
.fetchItems();
});
}
For your initMapState() function:
void initMapState() async {
await getCurrentLocation().whenComplete(() async {
await initMarkers().whenComplete((){
setState(() {
_loaded = true;
});
});
});
}
Keep in mind that, in your code, you are not working with the returning value of your getCurrentLocation() function, so instead of using the "then" method use the "whenComplete" method, assuring that you changed or returned your values with this function. Finally, for the initState(), make the function body with asynchronous:
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) async {
initMapState();
});
}
This should work.

Related

Flutter ensure I have a value in Async/Await and init functions [duplicate]

This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 20 days ago.
How can I make sure I have a state variable available after an async function call? My belief is because getValues() is async, it should "wait" until moving on to the next line. Thus, getValues() shouldn't exit and configValue() shouldn't be invoked until after my call to setState has finished. However the behavior I'm seeing it that values is an empty array in my Widget.
late List values = [];
#override
void initState() {
super.initState();
getValues();
configValue();
}
getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
// How to make sure I have values[0] here?
}
Thanks in advance!
You can change your getValues to this:
Future<List> getValues() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
return vals;
}
then create another middle function like this:
callasyncs() async {
var result = await getValues();
configValue(result);
}
and call it inside initState like this:
#override
void initState() {
super.initState();
callasyncs();
}
also change your configValue to this:
void configValue(List values) {
// now you have updated values here.
}
here your both configValue and getValues are separated from each other and also your configValue will wait for the getValues result.
you need to use await before the method to complete the future. also can be use .then.
Future<void> getVids() async { //I prefer retuning value
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() async {
await getVids();
}
Try the following code:
List? values;
#override
void initState() {
super.initState();
getValues();
configValue();
}
Future<void> getVids() async {
final String response = await rootBundle.loadString('assets/values.json');
final vals = await json.decode(response)['values'];
setState(() {
values = vals;
});
}
void configValue() {
if (values != null) {
if (values!.isNotEmpty) {
…
}
}
}

Flutter Using async await requires hotreload but using .then doesn't

Can anyone help me understand this piece of code:
String? userName = "";
String? userEmail = "";
AuthService authService = AuthService();
#override
void initState() {
// TODO: implement initState
super.initState();
gettingUserData();
}
while defining gettingUserData(), using async, await needs hotreload to show the email
gettingUserData() async {
setState(() async {
userName = await HelperFunction.getUsername();
userEmail = await HelperFunction.getUseremail();
});
}
But defining it using .then doesn't need hot relaod
gettingUserData() {
HelperFunction.getUseremail().then((value) {
setState(() {
userEmail = value;
});
});
HelperFunction.getUsername().then((value) {
setState(() {
userName = value;
});
});
}
Can anyone help me understand why this is?
The two versions are not equivalent. The Future.then version calls setState after each Future completes.
The await version calls setState with an asynchronous callback, which setState does not expect. Since setState expects a VoidCallback argument, it expects its callback to complete synchronously, and it will not be awaited. setState therefore executes and returns immediately before waiting for either of the Futures complete.
One way to correct your await version is to await the Futures first and to then call setState:
Future<void> gettingUserData() async {
var userName = await HelperFunction.getUsername();
var userEmail = await HelperFunction.getUseremail();
setState(() {
this.userName = userName;
this.userEmail = userEmail;
});
}

making new request in dio by removing first request

I have a function which makes a api call, if i call that api twice, i want to cancel first call i'm trying to use dio for this
import 'package:dio/dio.dart';
final _dio = Dio();
CancelToken abc = CancelToken();
Future<void> makeSearchQuery(String searchTerm) async {
abc.cancel();
return _dio.get(searchTerm, cancelToken: abc).then((value) {
print(value);
}).catchError((e) {});
}
void main() async {
await makeSearchQuery("https://reqres.in/api/products/3");
await makeSearchQuery("https://reqres.in/api/products/3");
}
The problem is i'm not getting any response for either of that, though i should get the response for second call
P.S actually I'm calling this function in onPressed of a button if that matters.
What you need is a debouncer,
class Debouncer {
final int milliseconds;
Timer? _timer;
Debouncer({
this.milliseconds = 500,
});
void run(VoidCallback action) {
if (_timer != null) {
_timer?.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
use run method to call your function.
when timer gets canceled your function won't get called

Flutter initState wait for async function to complete

in my main.dart i have among others those two functions:
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
}
And
#override
void initState() {
super.initState();
_fetchMasterData();
}
What i would like to have is to wait in initState till _fethcMasterData is done bevore Widgert build is called.
Is that possible? Many thanks for any help!
Here how I use an async func in initstate;
builder() async {
favoriteDatabase =
await $FloorFavoriteDatabase.databaseBuilder('favorite_database.db')
.build();
setState(() {
favoriteDao = favoriteDatabase.favoriteDao;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) =>
getNamePreferences().then(updateName));
});
builder();
favoriteDao.findAllMoviesAsStreamW();
favoriteDao.findAllMoviesAsStream();
}
Also you can check this mini article too.
It is not possible to await in initState, so when you finish all loading process then you can call SetState method which populate your widget with actual data.
Second solution could be use of futurebuilder or streambuilder where you want to show data but it is only possible if any methods data is not dependent on each other.
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
SetState((){}); // added line
}

Flutter: 'Future.wait' multiple async functions in parallel VS 'await' one at a time. <= different results

I recently learned of the fabulous way of waiting for multiple async functions to complete using Future.wait([asyncFuncOne(), asyncFunctwo()])
However, I noticed two different outcomes when running either of these blocks of code. One awaiting each function to finish, the other using Future.wait for parallel processing. What am I doing wrong?
Method 1:
await msm.initProfileData();
await msm.initActivityFeed();
await msm.getRecentlyActiveUsers();
await msm.getRecommendedUsers();
await msm.getGroups();
await msm.getFollowing();
await msm.getFollowers();
Method 2:
await Future.wait([
msm.getFollowing(),
msm.initProfileData(),
msm.initActivityFeed(),
msm.getRecentlyActiveUsers(),
msm.getRecommendedUsers(),
msm.getGroups(),
msm.getFollowers(),
]);
in Method 1, all the async functions complete before my apps home screen appears. In Method 2 the home screen appears before all the async functions complete.
Cheers and thanks in advance.
EDIT: Additional code example.
#override
void initState() {
super.initState();
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
setState(() => _showSignIn = true);
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) async {
if (account != null) {
await createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
_showSignIn = true;
});
}
}
createUserInFirestore() async {
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
//...
//do stuff
//...
await someFunc1(); //Method1
// await comeFunc2(); //Method2
//do more stuff
}
someFunc1() async {
msm.asyncfunc1();
msm.asyncfunc2();
}
someFunc2() async {
await Future.wait([
msm.asyncFunc1(),
msm.asyncFunc2(),
]);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
Using Future.wait(List<Future>) will wait for all the async operations without sequence as mentioned in the docs. While using await consecutively, it'll wait for the first await async operation to finish before running the next await async operation. If you have a prerequisite output before running the next async operation, it's better to use await async in sequence instead.