Issue adding element to list - firebase

I'm a total newbie to Flutter and I'm trying to add some data from Cloud Firestore to a list in Flutter, but having issues. I try to add the element, but after executing, the element isn't there. It's not throwing an exception or anything either. Maybe someone else has some advice for me!
I have tried changing the type of list (capture the doc from Cloud Firestore instead of data within the doc, same issue), I have also debugPrinted the data I am trying to store to make sure it exists, it does. I have done basic troubleshooting like running flutter clean as well. I am on the latest version of Flutter.
Firestore db = firestore();
List<String> getString() {
var dataList = new List<String>();
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
});
debugPrint(dataList.first);
return dataList;
The list is empty, though it should contain the "First name" field on this Cloud Firestore doc. Again, verified the data does exist and prints when calling debugPrint.

The db.collection('Users').get() is a async function, so debugPrint(dataList.first); executes before of the end of your firestores get, because that your array returns empty.
If you try it:
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
});
You will see your data.
You can use await to wait the call finishes, so you must return a Future and use async key word on function declaration. This is a conceipt that you must know of flutter async functions (Async Flutter). So, the code below can solve your problem.
Firestore db = firestore();
Future <List<String>> getString() async {
var dataList = new List<String>();
var result = await db.collection('Users').get();
result.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
return dataList;
}

Related

Why does persistenceEnabled: true result in my queries giving incorrect results?

I am using Firestore in my Flutter app. When I query a collection, I am getting the incorrect number of documents back.
The correct number of documents I should be getting back for my query is 20.
If I initialise Firebase as follows...
await Firebase.initializeApp();
FirebaseFirestore.instance.settings = Settings(persistenceEnabled: true);
I get only 2 documents back from my query.
If I initialize Firebase with peristenceEnabled false...
await Firebase.initializeApp();
FirebaseFirestore.instance.settings = Settings(persistenceEnabled: false);
I am wondering if it has to do with the fact I am only grabbing the first event in the stream. My query is as follows...
static Future<List<String>> myQuery(String personId , String bagId , String batchId , List<String> items) async {
var db = FirebaseFirestore.instance;
var q = db.collection('people')
.doc(personId)
.collection('bags')
.doc(bagId)
.collection('batches')
.where('batchId', isEqualTo: batchId)
.where('itemId', whereIn: items)
.where('status', isEqualTo: 'active');
var stream = q.snapshots().map((snapshot) {
List<String> results = [];
for (var doc in snapshot.docs) {
results.add(doc.id);
}
return results;
});
return stream.first;
}
}
If persistence is enabled this method returns a list of incorrect length. If persistence is disabled, this returns a list of the correct length.
I would expect the built in firestore caching mechanism would be smart enough to detect that any cached data is stale. I am therefore wondering if there is something wrong with my firestore data in general, such that it is breaking client side persistence/caching.
If you call snapshots() on a query, the Firestore SDK immediately invoked your callback with whatever data it has in the cache for that query (if any). It then checks with the server for any updates to the data, and (if any) invokes you callback again with the latest data.
But since you then call first() on the stream, you are only getting that first data from the local cache, and not the data from the server. If you only care about the current data, you should use get() instead of snapshots(), as that will first check for updates from the server before it invokes your callback.
So:
var snapshot = await q.get();
List<String> results = snapshot.map((doc) {
return doc.id;
});
return results;

Flutter-Firestore: - Code to retrieve data from firestore then save/use it

I am very new to Dart, and coding in general. I have produced this code after watching tutorials on YouTube. For the most part, I have been able to troubleshoot most of my problems on my own, here I feel I need some help. I wanted to extract all the fields from a document and use it. I have tried a few codes but there is no proper solution anywhere online.
Here is the code I used to retrieve it:-
documentID = '9zjwixClgwR1Act1OlPK'
firebaseGetData(documentID){
firebaseFirestore.collection('course').doc(documentID).get().then((value) {
print(value.data());
});
}
Here is my database file structure:-
I want to store all the fields in variables and use them. please help me with the correct code, please.
There are two ways to retrieve data stored in Cloud Firestore. Either of these methods can be used with documents, collections of documents, or the results of queries:
Call a method to get the data
const docRef=doc(db,’course’,'9zjwixClgwR1Act1OlPK')
getDoc(docRef)
.then((doc) => {
console.log(doc.data(),doc.id)
})
Set a listener to receive data-change events.
To get real-time data when you set a listener, Cloud Firestore sends your listener an initial snapshot of the data, and then another snapshot each time the document changes.
const docRef=doc(db,’course’,'9zjwixClgwR1Act1OlPK')
onSnapshot(docRef,(doc) => {
console.log(doc.data(),doc.id)
})
For more information, kindly check link1 & link2
Firstly you need to create firestore instance. Your function must be async and return a Future value. Also, you can check this document.
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<Map<String, dynamic>> firebaseGetData({required String documentID}) async {
DocumentSnapshot ds =
await _firestore.collection("course").doc(documentID).get();
Map<String, dynamic> data = ds.data() as Map<String, dynamic>;
print(data["videoDescription"] as String); // check if it null or not
return data;
}
// creating a instance variable
final CollectionReference firestoreInstance =
FirebaseFirestore.instance.collection('course');
void _loadUserData() async {
await firestoreInstance.doc(documentID).get().then((event) {
// you can access the values by
print(event['isDOne']);
print(event['lastViewedOn']);
print(event['lectureNo']);
print(event['videoDescription']);
print(event['videoUrl']);
});
}
call the _loadUserData() function whenever you need to fetch the data.

flutter firstWhere not responding (firestore)

I did a function a few months ago where my application is waiting for the user documents and responding accordingly. It was working like a charm until I optimized and updated the project to the last version.
If there is a user document, the stream yields the document and closes the stream.
If there is no user data in the cloud firestore, the stream yields null and awaits for the document to appear in the cloud.
// this function doesn't work properly and it should work but `firstWhere` is not
// listening to the stream unless there is a listener already which makes no sense
static Stream<DocumentSnapshot> get getUserDocumentWhenExists async* {
User user = FirebaseAuth.instance.currentUser;
if (user == null) throw 'No user is signed in';
FirebaseFirestore firebase = FirebaseFirestore.instance;
CollectionReference usersCollection = firebase.collection('users');
Stream<DocumentSnapshot> userDocumentStream = usersCollection.doc(user.uid).snapshots();
userDocumentStream.listen((event) {}); // <= this is here for the code to work
DocumentSnapshot userDocument = await userDocumentStream.first;
if (userDocument.exists == false) {
yield null;
yield await userDocumentStream.firstWhere((userDocument) {
// not beeing called without a previous listener
return userDocument.exists;
});
} else {
yield userDocument;
}
}
If you run this code without removing userDocumentStream.listen((event) {}) it will work without a problem as it did before the update.
My questions are:
Is this a bug?
Why is this happening? or did I just wrote something wrong?
Edit: I made a custom test without firebase and everything works fine. Just in this particular case firstWhere() is not listening to the stream
Edit2: after some more testing I discovered that any listener after userDocumentStream.first will not work. Now I'm more confused and I really need some help
I think after first() is called, the subscription is canceled even if in the first documentation it says otherwise:
"Internally the method cancels its subscription after the first element. This means that single-subscription (non-broadcast) streams are closed and cannot be reused after a call to this getter."
My solution:
Create a snapshots() stream for the first() and one for firstWhere()
final documentReference = usersCollection.doc(user.uid);
final userDocument = await documentReference.snapshots().first;
if (userDocument.exists == false) {
yield null;
yield await documentReference.snapshots().firstWhere((userDocument) {
return userDocument.exists;
});
} else {
yield userDocument;
}

async operation within an async function

For an instance, let's suppose I am calling a function in initState which gets some documents from the Firebase collection. I am iterating on those documents using async forEach and need to perform an await operation to get some data from another collection from firebase and saving them in a list then returning after the forEach is finished. But the returned list is empty as the second await function completes after return statement. How do I handle this? I have a hard time understanding Asynchronous programming so please a detailed explanation will be highly appreciated.
The code here is just an example code showing an actual scenario.
Future getList() async {
//These are just example refs and in the actual refs it does print actual values
var collectionOneRef = Firestore.instance.collection('Collection1');
var collectionTwoRef = Firestore.instance.collection('Collection2');
List<Map<String, dynamic>> properties = [];
QuerySnapshot querySnapshot = await collectionOneRef
.getDocuments()
.then((query) {
query.documents.forEach((colOneDoc) async {
var colOneID = colOneDoc.documentID;
await collectionTwoRef.document(colOneID).get().then((colTwoDoc) {
Map<String, dynamic> someMap = {
"field1": colTwoDoc['field1'],
"field2": colTwoDoc['field2']
};
properties.add(someMap);
properties.sort((p1, p2) {
//sorting based on two properties
var r = p1["field1"].compareTo(p2["field1"]);
if (r != 0) return r;
return p1["field2"].compareTo(p2["field2"]);
});
print(properties); //this prints actual data in it
));
});
});
});
print(properties); //This prints a blank list as [] and obviously returns blank list
return properties;
}
And now when I call this function in an initState of a stateful Widget and display it somewhere, it display a blank list. BUT, after I "Hot Reload", then it displays. I want to get the data and display it without hot reloading it. Thanks in advance
Sounds like you need to await the whole function itself in the block of code where you are calling getList().
you are async'ing and awaiting inside the function, and that looks fine which is why you are getting results when hot reloading. But it might be the case that you're not actually awaiting the entire function itself in the code that is calling this. Please Check.

How to properly get data from Firebase Firestore in Flutter?

I have a Firebase Firestore database as you can see in the picture. I'd like to get every document from the questions collection with their fields.
I found the following solution on the internet:
//in this list we store the data
List adatok = [];
Future<void> getData(){
databaseReference.getDocuments().then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f){
data = f.data;
adatok.add(data);
print(adatok.length);
print(adatok);
}
);
}
);
}
My problem is that when I call this function (in an onPresed() function of a button), for the first time of pressing the adatok list is empty. But when I press the button for the second time, it contains every data which I wanted to acquire.
Did I do something wrong? Is there another way to get the data?
The layout of the database
Use the code below:
List adatok = [];
Future<void> getData() async{
QuerySnapshot snapshot = await databaseReference.getDocuments();
snapshot.documents.forEach((f){
data = f.data;
adatok.add(data);
print(adatok.length);
print(adatok);
}
}
By this way, The program waits until it gets all the documents.
and then adds it to your snapshots.
Also you have to change your onPressed() function to async/await like the following:
onPressed:() async{
await getData();
/...
}
So the program waits until getData() finishes.

Resources