I cannot access the data when extracting data from firebase in Flutter - firebase

My code is written like this, but I cannot access the data in the output I get?
void getMessages() async {
final messages = await _firestore.collection('mesajlar').get();
for (var message in messages.docs) {
print(message.data);
}
}

The problem is that you are calling data as a property when it is actually a function, you can see that in the documentation for DocumentSnapshot, which QueryDocumentSnapshot implements. So all you have to change is the print:
print(message.data());

Related

"The method 'cast' isn't defined for the type 'Object'" when reading from Firebase

I create banner in listview and get the data from firebase database.
Future<List<String>> getBaners(DatabaseReference bannerRef){
//The method 'cast' isn't defined for the type 'Object'.
return bannerRef.once().then((snapshot) => snapshot.snapshot.value!.cast<String>().toList());
}
Try using
Future<List<String>> getBaners(DatabaseReference bannerRef)async{
final DataSnapshot res = await bannerRef.get();
if(res.exists)
return res.value as List<String>;
return [];
}
I am willing to help more if you can provide more details about the structure of the data that bannerRef is pointing to.

Kotlin Save Firestore query result for a variable

I have a problem with Firebase.
I also use Firestore on a website and in a Kotlin app.
On the website, I can save the result of the query to a variation in the following way:
const addStudentManu = async($this) => {
const userId = await db.collection('users').where('neptun','==',ASD123).get();
const getUserId = userId.docs.map(doc=>doc.id);
}
How can i do this in kotlin?
This is how it goes:
db.collection("users")
.whereEqualTo("neptun", "ASD123")
.get()
.addOnSuccessListener { documents ->
val list = mutableListOf<String>()
for (document in documents) {
Log.d(TAG, "${document.id}")
list.add(document.id)
}
println(list)
}
.addOnFailureListener { exception ->
Log.w(TAG, "Error getting documents: ", exception)
}
You can checkout the sample code snippets in the documentation.
While #Dharmaraj answer will work perfectly fine, when it comes to Kotlin, the most convenient way for saving the result of a query would be to use Kotlin Coroutines, We can create a suspend function and map all documents to their corresponding IDs, similar with your example. So please try the following lines of code:
private suspend fun getIdsFromFirestore(): List<String> {
val ids = db.collection("users").whereEqualTo("neptun", "ASD123").get().await()
return ids.documents.mapNotNull { doc ->
doc.id
}
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. That's almost the same thing when using async on the web.
Now we can simply call this from another suspend method like in the following lines of code:
private suspend fun getIds() {
try {
val ids = getIdsFromFirestore()
// Do what you need to do with the list of IDs
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}

How many "read"s does it cost to call get() function when fetching data from Firestore

I have the following code piece in a function that I call when I need to fetch the user profile data.
FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future getUserProfile() async {
try {
DocumentSnapshot ds =
await _firestore.collection('users').doc(_auth.currentUser.uid).get();
return ds;
} catch (e) {
print(e);
return null;
}
}
With the assumption that user's profile data does not change, does this call cost me 1 read each time I call the getUserProfile() function? If yes, how can I change this function so that my function only listens to changes and does not necessarily increase the number of reads in firestore?
Yes, each function call will cost you 1 read. If the data does not change through out the app you could fetch it at the start of your application and store it by creating a class say User, then add data to that User object. This method is very useful and would minimize the number of function calls made to fetch data.
Each time you call getUserProfile(), the read counter will increase by one.
Firestore offers an alternative to get() for listening to real time changes. It's called snapshots(), it returns a Stream<QuerySnapshot>. You can attach a listener and every time one of the documents you listen to, changes, it will be added to the stream. Initially all items matching your query (in your case it's only one) will be added to the stream. Your code should be then:
Stream<QuerySnapshot> getUserProfile() {
try {
Stream<QuerySnapshot> stream = _firestore
.collection('users')
.doc(_auth.currentUser.uid)
.snapshots();
return stream;
} catch (e) {
print(e);
return null;
}
}
Every time a change is added to the stream, the read will be increased by one.
More information can be found at the official Firestore Docs.

How do i modify the code to have a Stream<dynamic> type stream for stream builder?

Inside Stream builder, it is asking me to put Stream<builder> otherwise throwing an error,
type 'Future<dynamic>' is not a subtype of type 'Stream<dynamic>'
Now since I was following this answer, i had to code the stream something like this,
getGroupsOfUser() async {
String _userID = await _getUID();
DocumentSnapshot userInfo = await userCollection.document(_userID).get();
return groupCollection.where(FieldPath.documentId, whereIn: userInfo.data['groups']).snapshots();
}
Now I know maybe if I could overcome using async I may fix it but I need to get the uid and the array of groups which is an async function and I can't really assign them as variables otherwise I'm getting the error only static members can be accessed in initializers
Please, someone, help me I am very beginner in this.
getGroupsOfUser() should return a Stream since snapshots() returns a Stream. Therefore you need to do the following:
Stream<QuerySnapshot> getGroupsOfUser() async* {
String _userID = await _getUID();
DocumentSnapshot userInfo = await userCollection.document(_userID).get();
yield* groupCollection.where(FieldPath.documentId, whereIn: userInfo.data['groups']).snapshots();
}
Since you need to return a stream then you need to use async* and use the yield* keyword to return.
The error must be from the line
return groupCollection.where(FieldPath.documentId, whereIn: userInfo.data['groups']).snapshots();
Because .snapshots() method returns a Stream instead of a Future, and since your getGroupsOfUser() is a Future, it is throwing an error that you cannot return a Stream from a function that returns a Future.
My solution:
1 - Put the logic for getting the userID and the userInfo inside the initState() i.e make a Future for it.
2 - Seperate the last line from the Future and wrap it with a StreamBuilder after successfully getting the userinfo from step 1.

Flutter firebase database.set(object) issue

I have a class Product and it is in List plist
Now I need to call the firebase database.set(plist) this is working with Java but when I tried to do it with flutter dart it showing error anybody have the solution for this problem
From StackOverflow, I understand use database.set('{"a":"apple"}) but when I am dealing with List I can't use this solution
update error message
error called Invalid argument: Instance of 'Product'
My code
String table_name="order";
FirebaseAuth.instance.currentUser().then((u){
if(u!=null){
FirebaseDatabase database = FirebaseDatabase(app: app);
String push=database.reference().child(table_name).child(u.uid).push().key;
database.reference().child(table_name).child(u.uid).child(push).set( (productList)).then((r){
print("order set called");
}).catchError((onError){
print("order error called "+onError.toString());
});
}
});
}
We cannot directly set object in Firebase. Unfortunately in Flutter there is no easy solution like java json.
Data types that are allowed are String, boolean, int, double, Map, List. inside database.set().
We can have a look at the official documentation of Flutter https://pub.dev/documentation/firebase_database/latest/firebase_database/DatabaseReference/set.html
Try setting object like this
Future<bool> saveUserData(UserModel userModel) async {
await _database
.reference()
.child("Users")
.child(userModel.username)
.set(<String, Object>{
"mobileNumber": userModel.mobileNumber,
"userName": userModel.userName,
"fullName": userModel.fullName,
}).then((onValue) {
return true;
}).catchError((onError) {
return false;
});
}
I hope this code will be helpful.
Extending a little bit an answer given as a comment above
You basically have to create an auxiliary map beforehand:
Map aux = new Map<String,dynamic>();
And then iterate through the array that you have adding the corresponding map for each child that you want to add:
productList.forEach((product){
//Here you can set the key of the map to whatever you like
aux[product.id] = product.toMap();
});
Just in case, the function toMap inside the Product class should be something like:
Map toMap() {
Map toReturn = new Map();
toReturn['id'] = id;
toReturn['name'] = name;
toReturn['description'] = description;
return toReturn;
}
And then, when you are calling the set function to save to firebase you can do something like:
.set({'productList':aux,})
Hope this was helpful to someone.

Resources