Flutter initState wait for async function to complete - flutter

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
}

Related

Flutter: _TypeError

I'm trying to get datas from api and add them a list. But at this moment, I see datas i got but I can't get it out of the function. What should i do?
function
List<dynamic> xxx = [];
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}
When I run print(fetchCompanies(xxx)); I got "Instance of 'Future<List<dynamic>>'". How can i get data inside fetchCompanies to my xxx list?
You're trying to print future instance of List that's why you got
Instance of Future<List>
You have to wait until function finish executing.
Catch here is you can't call wait in initState() so you have to use .then method
try this:
fetchCompanies(xxx)
.then((result) {
print("result: $result");
});
It should already work fine like it is. But you probably want to call a setState to refresh the page. Try this:
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
setState(() {}); // added this
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}

Asynchronous method not running in proper order

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.

Flutter Try correcting the name to one that is defined, or defining the name

I made a function to post some data using http.post, when using the function i pass as parameter the id, now im using this function in other places but it says that it has 1 positional argument but 0 found.
Future<Response?> postLocationId(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/$id/workingTimeframes';
try {
response = await http.post(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
);
} catch (er) {}
return response;
}
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
futureDataForStatus = postLocationId(id); <<<< getting error here
}
ElevatedButton(
onPressed: () async {
final WorkingLocationData locationData =
workingData!.firstWhere(
(some) => some.id ==
chooseLocation);
await postLocationId(locationData.id); <<<
The main problem is because you are passed a parameter (id == null) that doesn’t exist before initState(),
If you try and change the id word for a '1' string, this work, but doesnt the better solution.
You need to modify your logic in your initState() because you pass an argument that is null you have a lot of options:
void initState() {
super.initState();
getLocationData():
}
void getLocationData() async {
final locationData = {...} // This return location data information
postLocationId(locationData.id)
}
or You can omit the end line in your initState()
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
// futureDataForStatus = postLocationId(id);
}
Edit your logic inside your onPressed function to this:
final locationData =
workingData!.firstWhere((some) => some.id == chooseLocation);
if (locationData != null) {
await postLocationId(locationData.id);
setState(() {
_flag = !_flag;
});
} else {
print('locationData is null);
}

Flutter : Flutter Shared Preference don't work ,not save username Edit#1

I have an app contain login and register system ,the system is working correctly.
the problem is I want to keep user logged in by using flutter_session package but don't work.
first I have preloading page contain checking if user is logged in by using functions below :
void gotoTabPae() {
print('ok');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('tabs');
});
}
void gotoLoginPage() {
print('no');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('login');
});
}
getuser() async {
var loginedUser;
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
loginedUser= preferences.getString('username');
});
loginedUser != null ? gotoTabPae() : gotoLoginPage();
}
#override
void initState() {
getuser();
super.initState();
}
when I run the app and login then when I restart the app it must go to 'tabs' page ,but the value of username is always null therefore it load login page ,the login function is :
login() async {
var formdata = formLoginKey.currentState;
if (formdata.validate()) {
formdata.save();
var data = {'username': username.text, 'password': password.text};
var url =xxxx/api/controller/users/login_user.php";
var response = await http.post(url, body: data);
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
} else {
_showDialog(context, responsebody['status']);
}
} else {
}
}
But in tabs page it is load the session username corret :
getuser() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var logineduser = preferences.getString('username');
}
#override
void initState() {
getuser();
super.initState();
}
How can I solve this? where is my mistake ?
Your code is running the getuser() method in the initSate() method that too even before the super.initState();. That is the reason the value is no able to load which makes it null. You should use it in the build function.
Your code might look like this:
#override
Widget build(BuildContext context) {
var loginedUser;
loginedUser = await FlutterSession().get('username');
loginedUser != null ? return ClassName()(tabs.dart) : return ClassName()(login.dart);
}
I think that in your login function, pref.setString('username', username.text) is run before the response is received. Can you try this:
http.post(url, body: data).then((response) {
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
}
});
and let me know the result?

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.