for-loop should wait for future - flutter

I have a list of userIDs and I want to get a value from the database for each user and write it to a new list. But the for loop doesn't wait for the future and throws the error "Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0"
List userIDs = ["gsdgsgsgda32", "gwerszhgda7h", "fsdgz675ehds"];
Future <dynamic> getList() async {
List items=[];
for (var i = 0; i < userIDs.length; i++) {
items[i] = await getUserItems(userIDs[i]);
}
return items;
}
Future <String?> getUserItems(String? _userID) async {
String? userItem=" ";
final FirebaseApp testApp = Firebase.app();
final FirebaseDatabase database = FirebaseDatabase.instanceFor(app: testApp);
database.ref().child('users').child(_userID!).once().then((pdata) {
userItem = pdata.snapshot.child('item').value as String?;
});
return userItem;
}

This is not problem with future. List items is empty so when you call items[0] = 3; there is no items[0] and you get RangeError. Proper way to add element to list is call items.add(3)
So your code should look like this:
List userIDs = ["gsdgsgsgda32", "gwerszhgda7h", "fsdgz675ehds"];
Future <dynamic> getList() async {
List items=[];
for (var i = 0; i < userIDs.length; i++) {
final item = await getUserItems(userIDs[i]);
items.add(item);
}
return items;
}
Future <String?> getUserItems(String? _userID) async {
String? userItem=" ";
final FirebaseApp testApp = Firebase.app();
final FirebaseDatabase database = FirebaseDatabase.instanceFor(app: testApp);
database.ref().child('users').child(_userID!).once().then((pdata) {
userItem = pdata.snapshot.child('item').value as String?;
});
return userItem;
}

By using .then you are telling dart to continue running and come back when the Future completes.
Instead you should use await inside getUserItems.
You have to fiddle around a bit but here's a suggestion to start with:
Future <String?> getUserItems(String? _userID) async {
String? userItem=" ";
final FirebaseApp testApp = Firebase.app();
final FirebaseDatabase database = FirebaseDatabase.instanceFor(app: testApp);
userItem = (await database.ref().child('users').child(_userID!).once()).snapshot.child('item').value as String?
return userItem;
}
Also using String? for userItem and setting it to " " is a bit of an anti pattern. Since you allow it to be nullable i'd suggest having it as null writing your logic around that.

Try to use it like this
Future <dynamic> getList() async {
List items=[];
userIDs.forEach((item) async {
items.add(await getUserItems(item));
});
return items;
}

Related

setter error in flutter while assigning value to class variable In flutter

I have created a demo where I am storing data in divide using sqfliteHelper and I want to create a object list to store data in memory for temporary use but I am getting error while assigning value to class variable, regarding setter required
here is my code
class SqfliteProvider {
List<CategoryModel> _categorylist = [];
List<CategoryModel> get categories {
return _categorylist;
}
static Future<List<CategoryModel>?> fetchcategory() async {
final db = await _getdb();
List<CategoryModel> templist = [];
List<Map<String, dynamic>> maplist = await db.query('categorytb');
if (maplist.isEmpty) return null;
templist = maplist.map((e) => CategoryModel.frommap(e)).toList();
//this statement showing an error ...required setter..
_categorylist = templist;
return templist;
}
}

How to Save List in SharedPreferences in Flutter

Hello all at first I want to mention that I've tried a lot of solutions here but it didn't work for me.
I bring the list from the database through the following code:
var listCat = [];
Future getdata() async {
apiURL = '***************.php';
var response = await http.post(Uri.parse(apiURL));
var responsebody = jsonDecode(response.body);
if (responsebody.length >0){
for (int i = 0; i < responsebody.length; i++) {
listCat.add(responsebody[i]['name']+ ':' + responsebody[i]['image'].toString());
}
return responsebody;
}else{
}
}
As is obvious in the code above I am trying to get the name and image and this is not a problem right now I want to store this listCat in SharedPreferences until I recall it from all pages of the app
I have the following class to save SharedPreferences:
class APIPreferences {
static SharedPreferences ? _preferences;
static const _keyMuinCat = 'MuinCat';
static Future init() async => _preferences = await SharedPreferences.getInstance();
static Future setMuinCat(String MuinCat) async => await _preferences!.setString(_keyMuinCat, MuinCat);
static String? getMuinCat() => _preferences!.getString(_keyMuinCat);
}
Then I save what I need to save by the following line:
APIPreferences.setMuinCat(listCat.toString());
Then I can bring pre-stored data from any location where I need it through the following code:
CatList = APIPreferences.getMuinCat() ?? '';
I tried to do the following thing now to save the list in the first code above:
var listCat = [];
Future getdata() async {
apiURL = '***************.php';
var response = await http.post(Uri.parse(apiURL));
var responsebody = jsonDecode(response.body);
if (responsebody.length >0){
for (int i = 0; i < responsebody.length; i++) {
listCat.add(responsebody[i]['name']+ ':' + responsebody[i]['image'].toString());
APIPreferences.setMuinCat(listCat.toString());
}
return responsebody;
}else{
}
}
But it didn't work. I don't really know how to deal with it.
How can I save it and then bring it to use with ListView.
instead of:
_preferences!.setString(_keyMuinCat, "some string");
use:
_preferences!.setStringList(_keyMuinCat, ["some", "strings", "in", "list"]);
So in your code, the setMuinCat method needs to be:
static Future setMuinCat(List<String> muinCat) async => await _preferences!.setStringList(_keyMuinCat, muinCat);
and then you call it like this:
APIPreferences.setMuinCat((listCat as List).map((v) => v.toString()).toList());
To save the list in shared preferences you need to pass as jsonEncode(yourList data) and when you will fecth the shared list you will again jsonDecode(your list)
await prefs.setString('YOUR KEY', json.encode(YOURMAP()));

How do I get a query from another file to return a bool?

I am integrating the following system into my to-do app:
Every time the user opens the app, it should check whether the date stored in Cloud Firestore has been exceeded.
If this is the case, all To-Dos of the user should be reset to false.
This is the date in Cloud Firestore I’m looking for:
This function should check if the date has been exceeded:
Future<bool> checkTime() async{
DateTime now = DateTime.now();
var query = users.where('Startdatum', isLessThanOrEqualTo: now);
query = query.where('userID', isEqualTo: userID);
final querySnapshot = await query.get();
return querySnapshot.size > 0;
}
And this function should reset all To-Dos to false:
Future allFalse() async{
return await users.doc(userID).get().then((DocumentSnapshot doc) {
var updateMap = new Map();
var toDos = doc['Level'];
for (var item in toDos.keys) {
updateMap[item] = false;
}
doc.reference.update({'Level' : updateMap});
});
}
I created both functions in a separate file (database), as you can see here:
class DatabaseService {
String userID;
DatabaseService(this.userID);
final CollectionReference users =
FirebaseFirestore.instance.collection('users');
Future allFalse() async {
return await users.doc(userID).get().then((DocumentSnapshot doc) {
var updateMap = new Map();
var toDos = doc['Level'];
for (var item in toDos.keys) {
updateMap[item] = false;
}
doc.reference.update({'Level': updateMap});
});
}
Future<bool> checkTime() async {
DateTime now = DateTime.now();
var query = users.where('Startdatum', isLessThanOrEqualTo: now);
query = query.where('userID', isEqualTo: userID);
final querySnapshot = await query.get();
return querySnapshot.size > 0;
}
}
I create an if condition in in inite State that includes checkTime. If checkTime returns true, the Future returns allFalse, which sets all To-Dos to false.
class _UebersichtState extends State<Uebersicht> {
User? user;
late DatabaseService database;
Future<void> connect() async{
final FirebaseAuth auth = FirebaseAuth.instance;
UserCredential result = await auth.signInAnonymously();
user = result.user;
database = DatabaseService(user!.uid);
}
#override
void initState() {
// TODO: implement initState
super.initState();
connect();
Future.delayed(Duration(seconds: 3), () async{
if(await database.checkTime()) {
return await database.allFalse();}
else print('Still time left');
});
}
I used a delay because the connect () function has to be executed first, it initializes database.
When I start the app, no error appears, but the To-Dos are not reset either.
Today we have the 21. 12. 2021 and in Cloud Firestore is 14. 12. 21 deposited.
The function allFalse works, it resets the To-Dos all.
It must be the function checkTime, which does not return a bool in the if condition. I just replaced it with if (0 == 0) and that triggers allFalse.
Can someone help me?
This is just a guess, but I believe this is the problem:
query = query.where('userID', isEqualTo: userID);
The above line would only work if your document had a field userID and said field was equal to your UID, but from what I could gather, you identify the UID by the name of the documents, if that is the case, this should work?
Future<bool> checkTime() async {
CollectionReference users = FirebaseFirestore.instance.collection('users');
final now = DateTime.now();
final doc = await users.doc(userID).get();
final stufenzeit = (doc.data() as Map<String, dynamic>)['Stufenzeit'] as Map<String, dynamic>;
final storedDate = (stufenSetit['Startdatum'] as TimeStamp).toDate();
return now.compareTo(storedDate) > 0;
}
There's probably also a way to do it with queries, but I am not so well versed on those to be completely honest.

Cannot get List data from SharedPreferences using FutureBuilder call

I have a String List in my shared preferences that's meant to store account numbers which can then be used to make a call from the API:
List<String> accountList = [];
Future _getListData() async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
for(var i = 0; i < myPrefs.getStringList('accounts').length; i++){
accountList.add(myPrefs.getStringList('accounts')[i]);
}
//prints values from shared preferences
print(accountList);
}
Future<List<dynamic>> fetchData() async {
_getListData();
//prints an empty list
print(accountList);
try {
if (accountList == null) {
var result = await http.get(apiUrl);
List<dynamic> accountInfo = (json.decode(result.body));
return accountInfo;
} else {
List<dynamic> accountInfo = [];
for(var i = 0; i < accountList.length; i++){
var result =
await http.get(apiUrl + "/api/Data/GetCustomer?accntnum=" + accountList[i]);
accountInfo.add(json.decode(result.body));
}
return accountInfo;
}
} catch (e) {
print(e);
}
}
When calling the Future function "fetchData()" from my FutureBuilder in the widget it returns an empty list. However within the scope of the "_getListData()" function it prints the list witht he appropriate values. How can I make it such that "fetchData()" gets the intended list?
Just add an await before _getListData() here:
Future<List<dynamic>> fetchData() async {
await _getListData();
//prints an empty list
print(accountList);

Dart List doesnt get updated with forEach loop

I am using this package to retrieve device's contacts. The lib retrieve 427 contacts and I want to loop the whole list so that I can create another list and send it to the back-end. The problem is looping does not work this the function return before looping is completed.
Here the function I use:
Future<QueryResult> uploadContacts() async {
final List<Contact> rawContacts =
(await ContactsService.getContacts(withThumbnails: false)).toList();
List<ContactInput> contactsListInput;
print('contactsListInput length: ${rawContacts.length}');
rawContacts.forEach((contact) {
print('contact: $contact'); //PRINTED JUST ONCE
//Contact can have more than 1 number. We need them all
contact.phones.forEach((phone) {
final contactInput =
ContactInput(name: contact.displayName, phone: phone.value);
contactsListInput.add(contactInput);
});
});
print('contactsListInput length: ${contactsListInput.length}'); //NEVER PRINT ANYTHING
final ContactsListInput input =
ContactsListInput(contacts: contactsListInput);
final MutationOptions _options = MutationOptions(
document: SyncContactsMutation().document,
variables: SyncContactsArguments(input: input).toJson());
return client.mutate(_options);
}
I have also tried using for loop and the same thing happened.
for (int i = 0; i < rawContacts.length; i++) {
final contact = rawContacts[i];
final contactInput =
ContactInput(name: contact.displayName, phone: contact.phones.first.value);
contactsListInput.add(contactInput);
}
print('contactsListInput length: ${contactsListInput.length}'); //NEVER CALLED
And I also tried Future.forEach
await Future.forEach(rawContacts, (contact) async {
print('contact: $contact');
//Since contact can have more than one number we loop them too.
await Future.forEach(contact.phones, (phone) async {
final contactInput =
ContactInput(name: contact.displayName, phone: phone.value);
contactsListInput.add(contactInput);
});
});
How to fix this? Any help will be much appreciated.
I have fixed it as
Future<QueryResult> uploadContacts() async {
final Iterable<Contact> rawContacts =
(await ContactsService.getContacts(withThumbnails: false));
final Iterable<ContactInput> contacts = rawContacts.expand((contact) => contact.phones.map(
(phone) =>
ContactInput(name: contact.displayName, phone: phone.value)));
final input = ContactsListInput(contacts: contacts);
final MutationOptions _options = MutationOptions(
document: SyncContactsMutation().document,
variables: SyncContactsArguments(input: input).toJson());
return client.mutate(_options);
}
Credit goes to #pskink and #loganrussell48
You should use it as a dynamic type object. Try something like this:
(event.snapshot.value as dynamic).forEach()
Try and see if it works.