How can I resolve a future within an async* stream in dart? - asynchronous

I have a stream of posts that works fine but I want to check whether the current user likes a particular post. To implement the latter, I have to get info from Firestore which is a future. I have tried using yield* but it doesn't work. My code is as below;
CollectionReference posts = FirebaseFirestore.instance.collection('posts');
String userId = FirebaseAuth.instance.currentUser?.uid;
Stream<List<Post>> get allPosts async* {
yield* posts.snapshots().map((snapShot) => snapShot.docs.map((document) {
DocumentSnapshot isLikedByCurrentUser = await FirebaseFirestore //The await here is erroneous and doesnt work
.instance
.collection('likes')
.doc(this.userId + '_' + document.id)
.get();
return Post.fromJson(
document.id, document.data(), isLikedByCurrentUser.exists);
}).toList());
}

This is more complicated than it needs to be. I'd unwrap the map calls to for loops:
Stream<List<Post>> get allPosts async* {
for (var snapshot in posts.snapshots()) {
var element = <Post>[];
for (var document in shapshot.docs) {
DocumentSnapshot isLikedByCurrentUser = await FirebaseFirestore
.instance
.collection('likes')
.doc(this.userId + '_' + document.id)
.get();
element.add(Post.fromJson(
document.id, document.data(), isLikedByCurrentUser.exists));
}
yield element;
}
}
You can even use a list literal, if you don't mind inlining the isLikedByCurrentUser variable:
Stream<List<Post>> get allPosts async* {
for (var snapshot in posts.snapshots()) {
yield <Post>[
for (var document in shapshot.docs)
Post.fromJson(
document.id,
document.data(),
(await FirebaseFirestore.instance
.collection('likes')
.doc(this.userId + '_' + document.id)
.get())
.exists)
];
}
}

This is how I have re-written it based on this answer by jamesdlin.
Future<List<Post>> _postFromSnapshot(QuerySnapshot snapshot) async {
var futures = snapshot.docs.map((doc) async {
DocumentSnapshot isLikedByCurrentUser = await FirebaseFirestore.instance
.collection('likes')
.doc(this.userId + '_' + doc.id)
.get();
return Post.fromJson(
doc.id, doc.data(), isLikedByCurrentUser.exists);
}).toList();
return await Future.wait(futures);
}
Stream<List<Post>> get allPosts {
return posts.snapshots().asyncMap(_postFromSnapshot);
}

Related

Flutter Firebase Reading Data

I have a variable called _memories and I want to update it to the content that is returned from the Firestore database in the .then() and then return it. I know the content is correct in from using the print statement, but neither return statement is returning the updated _memories. It's returning the {'title': 'myTitle'}. Anyone know how to fix this?
List getUserMemories() {
User? currentUser = AuthenticationService().getUser();
if (currentUser == null) {
throw Exception('currentUser is null');
}
CollectionReference memories = _firestore.collection('memories');
List _memories = [
{'title': 'myTitle'}
];
memories
.where('user_email', isEqualTo: currentUser.email)
.get()
.then((QuerySnapshot querySnapshot) async {
_memories = await querySnapshot.docs.map((e) => e.data()).toList();
print("In FirestoreService: $_memories");
return _memories;
})
.catchError((error) => print("Failed to obtain user's memories: $error"));
return _memories;
}
Try to convert getUserMemories to an async function and use await where you call it:
List getUserMemories() async {
User? currentUser = AuthenticationService().getUser();
if (currentUser == null) {
throw Exception('currentUser is null');
}
CollectionReference memories = _firestore.collection('memories');
List _memories = [
{'title': 'myTitle'}
];
try {
final result = await memories
.where('user_email', isEqualTo: currentUser.email)
.get();
_memories = result.docs.map((e) => e.data()).toList();
print("In FirestoreService: $_memories");
} catch (e) {
print("Failed to obtain user's memories: $e"));
}
return _memories;
}

How can I filter a map / Firestore Flutter

I need to search for data from a map in the "users" collection of a single document. But I only found ways to search all documents.
See below.
Code 1:
FirebaseFirestore.instance
.collection('users')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
Map<String, dynamic> res = doc["favorite"];
});
});
Code 2:
final String _collection = 'users';
final FirebaseFirestore _fireStore = FirebaseFirestore.instance;
getData() async {
return await _fireStore.collection(_collection).get();
}
getData().then((val) {
if (val.docs.length > 0) {
print(val.docs[0].data()["favorite"]);
} else {
print("Not Found");
}
});
Hello you can search in fields:
// Create a reference to the cities collection
var citiesRef = db.collection("cities");
// Create a query against the collection.
var query = citiesRef.where("state", "==", "CA");
See the Firestore queries reference here:
https://firebase.google.com/docs/firestore/query-data/queries

Flutter - type 'List<dynamic>' is not a subtype of type 'String'

I have a list of document id that I wanted to update. But when I use the list the error type 'List<dynamic>' is not a subtype of type 'String' happens.
My firebase code:
Future createGroupChat(
String groupName, String userName, memberList, memberIDList) async {
FirebaseFirestore.instance
.collection('users')
.where('username', isEqualTo: memberList)
.snapshots();
DocumentReference groupDocRef = await groupCollection.add({
'groupName': groupName,
'admin': [userName],
'members': memberList,
'groupID': '',
});
groupDocRef.update({
'groupID': groupDocRef.id,
'members': FieldValue.arrayUnion([userName])
});
DocumentReference userDocRef =
FirebaseFirestore.instance.collection('users').doc(userID);
userDocRef.update({
'groups': FieldValue.arrayUnion([groupDocRef.id + '_' + groupName])
});
DocumentReference memberDocRef =
FirebaseFirestore.instance.collection('users').doc(memberIDList);
return memberDocRef.update({
'groups': FieldValue.arrayUnion([groupDocRef.id + '_' + groupName])
});
}
My onPressed code to trigger createGroupChat:
onPressed: () {
if (groupNameController != null) {
DatabaseMethods(userID: user.uid).createGroupChat(
groupNameController.text,
Constants.myName,
selectedUsers,
memberIDList);
Navigator.push(context,
MaterialPageRoute(builder: (context) => GroupChatPage()));
}
},
I've tried #Sal Man's suggestion by passing index to memberIDList[index] using a for loop as below:
for (var i = 0; i < memberIDList.length; i++) {
DocumentReference memberDocRef =
FirebaseFirestore.instance.collection('users').doc(memberIDList[i]);
memberDocRef.update({
'groups': FieldValue.arrayUnion([groupDocRef.id + '_' + groupName])
});
*/
return print(memberIDList[i]);
}
But the for loop only iterates once even if I have three variables in the list.
Add index to membberIDList
Future<void> updateMethod({int index}){
DocumentReference memberDocRef =
FirebaseFirestore.instance.collection("user").doc(memberIDList[index]);
return await memberDocRef.update({
'groups': FieldValue.arrayUnion([groupDocRef.id.toString() + '_' + groupName])
});
}
memberIDList is of type List, doc method signature takes a String as a parameter. Try to pass an element of memberIDList which is of type String instead of passing the entire List as an argument.
If you want to get data for all elements of memberIDList, an option is to use Future.forEach method.
Future getDataByUserId(String userId) {
DocumentReference memberDocRef = FirebaseFirestore.instance.collection("user").doc(userId);
return memberDocRef.update({
'groups': FieldValue.arrayUnion([groupDocRef.id + '_' + groupName])
});
}
Future.wait(memberIDList.map(getDataByUserId)).then((List responses) => ... );

How to access data of a filter query in firestore flutter?

I want to search for the user from his mobile number. If the user found then show his other information like Name, Surname.
The search query is working fine. But I'm not able to access the data. Below is my function to get the result of the query. When I print the data its just prints [Instance of 'QueryDocumentSnapshot']
getData() async {
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('CommonData')
.where(
'Mobile_Number',
isEqualTo: mobileNumber,
)
.get();
final List<DocumentSnapshot> resultDocument = result.docs;
print(resultDocument);
}
Try this
getData() async {
String mobile_number;
String name;
String surname;
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('CommonData')
.where(
'Mobile_Number',
isEqualTo: mobileNumber,
)
.get();
result.docs.forEach((value) {
mobile_number = value.data()['Mobile_Number'];
name = value.data()['Name'];
surname = value.data()['SurName'];
});
print("Mobile Number: " + mobile_number);
print("Name: " + mobile_number);
print("SurName: " + mobile_number);
}
Since you make the assumption that your DB does contain one and only one User per Mobile Number, you might want to enforce this assumption:
Future<Map<String, dynamic>> getData(String mobileNumber) async {
return FirebaseFirestore.instance
.collection('CommonData')
.where(
'Mobile_Number',
isEqualTo: mobileNumber,
)
.get()
.then((snapshot) => snapshot.docs.single.data())
.catchError(
(e) {
if (e.message == 'No element') {
print("Couldn't find User for Mobile Number $mobileNumber");
} else if (e.message == 'Too many elements') {
print("Found duplicate Users for Mobile Number $mobileNumber");
}
return null;
},
test: (e) => e is StateError,
);
}

check if firebase record exist and return bool

Trying to check firebase record and perform subsequent logics,
Future<bool> isAlreadyThere(selectedPropertyId) async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
var myMapQuery = Firestore.instance
.collection("props")
.where('xxx', isEqualTo: xxxid)
.where('yyy', isEqualTo: user.uid);
var querySnapshot= await myMapQuery.getDocuments();
var totalEquals= querySnapshot.documents.length;
return totalEquals > 0;
}
and in the onTap() of widget ,
bool isThere=isAlreadyThere(suggestion.documentID) as bool;
if (isThere) {//do stuff here}
errors,
type 'Future' is not a subtype of type 'bool' in type cast
I know its the casting , but tried removing and some other ways as well , wont work.
await is missing in where the query
Future<bool> isAlreadyThere(selectedPropertyId) async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
var myMapQuery = (await Firestore.instance
.collection("props")
.where('xxx', isEqualTo: xxxid)
.where('yyy', isEqualTo: user.uid));
var querySnapshot= await myMapQuery.getDocuments();
var totalEquals= querySnapshot.documents.length;
return totalEquals > 0;
}
Use method like
isAlreadyThere(suggestion.documentID).then((value) {
if (value) {//do stuff here}
});

Resources