How to pass a List or specific looped list to firebase doc in flutter - flutter

I am trying to achieve a task in which I have a List<dynamic>and its giving me multiple values on its indexes e.g. ['Me','Admin', so on....] something like this.
I cannot pass the List directly to Document ID it gives index error and I don't if it will still give error or not If the List give data in string List<String>
I want to loop around the indexes of this list and pass it to Firebase collection's document id to get multiple data's of the users. For example on list's index 0 there's Me coming for myself and on index 1 there's Admin coming. Both have their respective data stored in Firestore collection with their own document id's Me and Admin. I want it to be checked on the runtime the app will check if its Me or Admin or Some other index value
Here's my code of the list and the firestore I'm trying to achieve.
List<dynamic> clientcodes = [];
void getclientcodes() async {
final clientcode = await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.email)
.get()
.then((clientcode) {
return clientcode.data()!["clientcode"];
});
setState(() {
if (clientcode != null) {
clientcodes = clientcode;
} else if (clientcode == null) {
setState(() {
const SpinKitSpinningLines(size: 100, color: Color(0xFF25315B));
});
}
});
}
Firestore:
Future getdatastatus() async {
DocumentSnapshot result = await FirebaseFirestore.instance
.collection("Statements")
// .doc("If I hardcode it the value of index 0 or 1 it works fine")
.doc(portfolionames.toString()) // This is area of issue
.get();
if (result.exists) {
print("Yes");
} else {
print("No");
}
}

You can insert getdatastatus() inside a loop, and let it get the index automatically by comparing it with any value you want it, see this:
Future getdatastatus() async {
for (var item in clientcodes) {
String docId = item.id;
if (docId == 'X' || docId == 'Y') {
DocumentSnapshot result = await FirebaseFirestore.instance
.collection("Statements")
.doc(docId)
.get();
if (result.exists) {
print("Yes");
} else {
print("No");
}
}
}
}
Hope that work with you!!
Update
In the first section of your code, I think there is a problem..
You can create the list out of the firestore streaming, then add the coming data to the list of model, after that you can loop it to take the value you want.
Class Database{
List<TestModel> clientcodes = [];
getclientcodes() {
return FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.email)
.snapshots()
.listen((event) {
clientcodes.add(TestModel.fromMap(event));
setState(() {
if (clientcode != null) {
clientcodes = clientcode;
} else if (clientcode == null) {
setState(() {
const SpinKitSpinningLines(size: 100, color: Color(0xFF25315B));
});
}
});
});
}
}
class TestModel {
late String name;
late String description;
TestModel({
required this.name,
required this.description,
});
TestModel.fromMap(DocumentSnapshot data) {
name = data['name'];
description = data['description'];
}
}

Related

How to fetch data and update it from firebase

I am having trouble trying to fetch data from firebase and updating the values from it.
I have a restaurant name and the number of times it has been picked (user chooses to go to that restaurant to eat). I am trying to retrieve the numPicked and update it by adding one if the user decides to go there again.
Here i am trying to fetch ONE specific document and trying to store the docID and the variables I need to update.
docID = doc.id; docID is return NULL
meaning that the foreach loop isn't even being read.
Future<bool> searchQuery(
{required String restaurantName,
required var userID,
required db}) async {
int addOne = 1; //addes one if it has been picked
//this is not working
try {
Query query2 =
db.where('userId', isEqualTo: FirebaseAuth.instance.currentUser!.uid);
Query query = query2.where('restaurantName', isEqualTo: restaurantName);
await query.get().then((querySnapshot) {
// ignore: avoid_function_literals_in_foreach_calls
querySnapshot.docs.forEach((doc) {
docID = doc.id;
numPicked = doc['numPicked'];
restaurantExist = true;
});
}).catchError((error) {
// print('error querying: #error');
});
} catch (ex) {
// ignore: avoid_print
print(ex);
}
//this is not working
int totalPicked = numPicked + addOne;
//if the restaurant exist then update the numpicked for that specific restaurant
if (restaurantExist) {
try {
var query = db
//.collection('NumRestaurantPicked')
.doc(docID);
await query.update({'numPicked': totalPicked.toString()});
} catch (ex) {}
}
return restaurantExist;
}
The docID and numPicked variables are not defined in the method signature, so they are not accessible outside of the try block. They should be defined as class variables, so they can be accessed from other methods.

How do I ensure that the list is populated before checking what it contains?

In the code snippet below, how do I ensure that the senderID list gets populated before checking what the list contains?
I'm pulling data from firestore, storing it in a list, and then checking if it contains a particular key.
Stream<List<Group>> getChatGroups(String grpId) {
return firestore.collection('groups').snapshots().map((event) {
List<Group> groups = [];
List<String> senderIds = [];
for (var document in event.docs) {
var group = Group.fromMap(document.data());
firestore.collection('groups').doc(group.groupId).collection('chats').get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((DocumentSnapshot doc) {
var messageData = doc.data() as Map<String, dynamic>;
var messages = Message.fromMap(messageData);
var grId = doc.reference.parent.parent?.id;
//The values in SenderIds should be set before the function below is initiaited
senderIds.add(messages.senderId);
});
});
//This if function should initiate after getting set above
if (senderIds.contains(auth.currentUser!.uid)) {
groups.add(group);
}
}
return groups;
});
}
If you want senderIds.contains to be called only after all of your Futures have completed, build a List of those Futures and use Future.wait on that List. Something like:
var futures = <Future<void>>[];
for (var document in event.docs) {
// ...
futures.add(
firestore
.collection('groups')
.doc(group.groupId)
.collection('chats')
.get()
.then((QuerySnapshot snapshot) {
// ...
}),
);
}
await Future.wait(futures);
if (senderIds.contains(auth.currentUser!.uid)) {
// ...
}
Note that since the above code is asynchronous, you should also be using asyncMap instead of map.

How to get a subcollection from a document firestore flutter

Im trying to fetch the documents from a subcollection which is in another document, and when I try to fetch the docs and fill a local list with the "docs data" it doesn't fill it, can anyone tell me what I'm doing wrong here?
My method of when I try to fetch the subcollection:
static Stream<List<CheckInOutModel>> employeeCheckInOutStream() {
return firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.snapshots()
.asyncMap((QuerySnapshot querySnapshot) {
final List<CheckInOutModel> employeesCheckInOutList = [];
for (final element in querySnapshot.docs) {
firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.doc(element.id)
.collection('checkInOutList')
.snapshots()
.asyncMap((QuerySnapshot query) {
for (final element in query.docs) {
final employeeCheckInOutModel =
CheckInOutModel.fromDocumentSnapshot(
documentSnapshot: element,
);
employeesCheckInOutList.add(employeeCheckInOutModel);
}
});
}
return employeesCheckInOutList;
});
}
My method when I fetch the fields of the documents that the subcollection is in:
static Stream<List<EmployeeModel>> employeeStream() {
return firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.snapshots()
.map((QuerySnapshot query) {
final List<EmployeeModel> employees = [];
for (final employee in query.docs) {
final employeeModel =
EmployeeModel.fromDocumentSnapshot(documentSnapshot: employee);
employees.add(employeeModel);
}
return employees;
});
}
So I figured out what I did wrong here, I tried to call a stream of it when I only needed it when a callBack is called, so I changed the logic accordingly and went with Future instead Stream
My updated code:
static Future<List<CheckInOutModel>> employeeCheckInOutStream({
required String id,
}) async {
final List<CheckInOutModel> employeesCheckInOutList = [];
final query = await firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.doc(id)
.collection('checkInOutList')
.get();
for (final employee in query.docs) {
final employeeCheckInOutModel = CheckInOutModel.fromDocumentSnapshot(
documentSnapshot: employee,
);
employeesCheckInOutList.add(employeeCheckInOutModel);
}
return employeesCheckInOutList;
}

Future returns list before being populated

I have the following method:
Future<List<Job>> getUserJobs() async {
Query query = _firebaseDatabase
.reference()
.child("jobs")
.child(_firebaseAuth.currentUser.uid)
.orderByKey();
List<Job> userJobs = [];
if (query == null) {
return userJobs;
}
query.onValue.listen((event) {
Map<dynamic, dynamic> values = event.snapshot.value;
values.forEach((key, value) {
userJobs.add(Job.fromJson(key, Map.from(value)));
});
});
return userJobs;
}
I want to get this response in another class, however, the list returned by the above method is always []. I checked and the userJobs list is indeed populated but the return statement is executed before.
The structure of the database is:
Job collection has user IDs and for each user ID I have several job keys (each with its job data).
Try this:
Future<List<Job>> getUserJobs() async {
List<Job> userJobs = [];
// Query query =
await _firebaseDatabase
.reference()
.child("jobs")
.child(_firebaseAuth.currentUser.uid)
.once()
.orderByKey().then((result) async {
if (result.value != null) {
result.value.forEach((key, childSnapshot) {
userJobs.add(Job.fromJson(key, Map.from(childSnapshot)));
});
} else {
print(
'getUserJobs() no jobs found');
}
}).catchError((e) {
print(
'getUserJobs() error: $e');
});
// if (query == null) {
// return userJobs;
// }
// query.onValue.listen((event) {
// Map<dynamic, dynamic> values = event.snapshot.value;
// values.forEach((key, value) {
// userJobs.add(Job.fromJson(key, Map.from(value)));
// });
// });
return userJobs;
}
your loop also needs to be async..otherwise the method will return before the loop finishes, returning the empty List.. been there and got quite frustrated by this..
also always use .catchError callback.. it tells you what's going wrong ;)

Getting document from Firestore to pun inside a Custom User profile but it returns null. Dart/Flutter

Hi there I'm having some trouble to retrieve data from Firestore;
I created a class User for getting all the information for my app.
User Class:
class User {
String id;
String displayName;
String email;
String pictureURL;
String gender;
DateTime dateOfBirth;
User(fbauth.User user) {
id = user.uid;
displayName = user.displayName;
email = user.email;
pictureURL = user.photoURL;
// setting info
DocumentSnapshot resl = FirestoreUtil.read("AdditionalUserInfo", user.uid);
gender = resl.data()['gender'];
dateOfBirth = (resl.data()['date of birth'] as Timestamp).toDate();
}
FirestoreUtils class:
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) {
FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get()
.then((DocumentSnapshot snapshot) {
if(snapshot.exists){
return snapshot;
}else{
print("no data found");
}
});
}
}
After I return the snapshot, it gets null. But if I do this it prints out the correct values:
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) {
FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get()
.then((DocumentSnapshot snapshot) {
if(snapshot.exists){
print(snapshot.data);
}else{
print("no data found");
}
});
}
}
I tried literally everything but I couldn't figure out. Could someone help? I cannot imagine that is that hard to retrieve data inside a class.
It's probably Future(async/await) problem. You are returning value inside Future, you can get this value inside another then() etc.
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) async {
// You can directly return it too without assigning it to result variable
var result = await FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get();
return result;
}
}
There is data() method for DocumentSnapshot, you can use result.data() and check if it's null, isEmpty etc.
I wrote this function with async/await but if you need Future and didn't want to wait operation to be finished, you can directly return
FirebaseFirestore.instance.collection(collection).doc(document).get();
and you can use then() after you get the Future.