Flutter - Snapshot from Sub-collection - flutter

I was reading several answers here but I could not get better understanding to solve my issue which is the following...
I don't know how to get the "docId" in the code below...
I want to query a sub-collection "People" as snapshot for the stream method which I will listen using Bloc...
I am not able to understand how to get the docId. Anyone could help me with this?
Thanks
Stream<People> statusMember({required String email}) {
var user = auth.FirebaseAuth.instance.currentUser;
final snap = _firestore
.collection('users')
.doc(user!.email)
.collection('people') //sub-collection
.doc(docId) // docId as reference? How to get it?
.snapshots()
.map((snapshot) => People.fromSnapshot(snapshot)); // model
print(snap);
return snap;
}

By digging more into the Firebase documentation, it is not possible to read docs in a sub-collection the way I wanted. Therefore, I've changed my approach and the code looks like this:
Stream<People?> statusMember({required String email}) {
var user = auth.FirebaseAuth.instance.currentUser;
final snap = _firestore
.collection('users')
.doc(user!.email)
.collection('people')
.where('email', isEqualTo: email)
.snapshots()
.map(
(snappshot) {
for (var doc in snappshot.docs) {
return People.fromSnapshot(doc);
}
},
);
print(snap);
return snap;
}
it is working as expected.

Related

How to return a Stream using an async* function in Flutter

I am working on a chat app using Flutter and Firebase. I am new to Dart and so got stuck when I wanted to create a function which fetches (using await) a particular document from one collection (forums) and use an array property of the forum document to query and return a Stream from another collection (openMessages). The problem with my current solution is that it always returns an empty array. I am sure I am using the keywords or logic incorrectly. Can you please help me refactor my method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots()
.map((event) {
for (var document in event.docs) {
messages.add(ChatMessage.fromMap(document.data()));
}
});
//print('LENGTH:'+messages.length.toString());
yield messages;}
You can use the following method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
final result = firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots();
await for (final r in result) {
final docs = r.docs;
for (final document in docs) {
messages.add(ChatMessage.fromMap(document.data()));
yield messages;
}
}
}
Or
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
yield* firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSend', descending: true)
.snapshots()
.map((event) =>
event.docs.map((e) => ChatMessage.fromMap(e.data())).toList());
}

How to delete data from Firestore with specific parameter flutter?

I'm trying to delete some data with where parameter to specify which one should be deleted.
I've this method and it works perfectly.
final FirebaseFirestore _db = FirebaseFirestore.instance;
Future deleteInstructorDocumentInFirestore(String url) async {
_db.collection(instructorsDocument)
.where("photo", isEqualTo: url)
.get()
.then((value) {
for (var element in value.docs) {
_db.collection(instructorsDocument)
.doc(element.id)
.delete();
}
});
}
So I've tried the same thing with this one but it doesn't work.
Firestore
Function
final FirebaseFirestore _db = FirebaseFirestore.instance;
Future deleteEvent(String uid, DateTime from, DateTime to) async {
print("From: $from");
print("From type: ${from.runtimeType}");
print("To: $to");
print("To type: ${to.runtimeType}");
_db.collection(instructorsEvent)
.where("uid", isEqualTo: uid)
.where("from", isEqualTo: from)
.where("to", isEqualTo: to)
.get()
.then((value) {
for (var element in value.docs) {
_db.collection(instructorsEvent)
.doc(element.id)
.delete();
}
});
}
What am I doing wrong.
Thanks in advance
The from and to fields are strings. But you are trying to compare them to dart DateTime object, which are not strings. As such, they will not match in any query - your queries are simply returning no documents at all, so there is nothing to delete. (I suggest adding some debug logging to better observe this for yourself.)
Firestore will not convert DateTime objects to strings for you, nor will it try to guess how to compare them for you. You must provide a similar type of value to the document field you are trying to compare to.
So, if your document fields must remain strings, then you should find a way to convert your DateTime objects to strings before handing them to a Firestore query. Or, you must find another way of representing points in time (in Firestore, usually a timestamp type field), then provide objects to the query that Firestore naturally understand to be comparable to timestamps.
See also:
Flutter: query Firestore document by timestamp
How to query for an entire day with timestamp values in Firestore
Problem Solved
#Doug Stevenson thanks
Utils
static String toDateTimeToStringFromFirestore(DateTime dateTime) {
final date = DateFormat("yyyy-MM-ddTHH:mm:ss.SSS").format(dateTime);
return date;
}
EventViewModel
Future deleteEvent(String uid, DateTime from, DateTime to) async {
final fromString = Utils.toDateTimeToStringFromFirestore(from);
final toString = Utils.toDateTimeToStringFromFirestore(to);
_db.collection(instructorsEvent)
.where("uid", isEqualTo: uid)
.where("from", isEqualTo: fromString)
.where("to", isEqualTo: toString)
.get()
.then((value) {
for (var element in value.docs) {
_db.collection(instructorsEvent)
.doc(element.id)
.delete();
}
});
}

flutterfire where and orderby not return data

i have implemented this code for retrieving the messages of this room.
final messagesProvider = StreamProvider((ref) {
FirebaseFirestore db = FirebaseFirestore.instance;
var room = ref.watch(roomIdProvider);
print('room updated');
print('room is '+room);
final docRef = db
.collection("messages")
.where("chat_room_id",isEqualTo: room)
// .orderBy('created_at')
// .orderBy('created_at',descending: true)
;
print(docRef.orderBy("created_at").snapshots());
return docRef.snapshots();
});
i want to sort the data and have tried these two lines separately but not worked for me
.orderBy('created_at')
.orderBy('created_at',descending: true)
where created at is a timestamp field.
I added a new collection called "school", there're two items added inside the document.
I used my code, and it works. Could you please remove ".where" and try it again?
void getMessagesTest() async{
QuerySnapshot querySnapshot = await _firestore.collection('school').orderBy('age',descending: true).get();
final allData = querySnapshot.docs.map((doc) => doc.data()).toList();
print(allData);
}
Updated 20220616:
Updated 20220618:
Yes, you could chain where and orderBy together. Please see my code below.
Reference link => Using Where and Order by different fields in Firestore query
void getMessagesTest() async{
QuerySnapshot querySnapshot = await _firestore.collection('school').orderBy('age', descending: true).where('age', isGreaterThan: 17).get();
final allData = querySnapshot.docs.map((doc) => doc.data()).toList();
print(allData);
}

How to get firestore data as stream?

I am making a collection group query, where upon matching a particular field, I am going a level backwards and then read the data.
I am able to do that in Future approach.
Here is my code returning future:
#override
Future<Either<JobPostFailure, List<JobPost>>> readAppliedJobPosts({
required String seamanId,
}) async {
final querySnapshot = await FirebaseFirestore.instance
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.get();
final List<JobPost> mList = [];
for (var docSnap in querySnapshot.docs) {
final jobPostDocSnap = await docSnap.reference.parent.parent?.get();
mList.add(JobPostDto.fromFirestore(jobPostDocSnap!).toDomain());
}
return right(mList);
}
Now I am struggling to do this in Stream approach, where my return type would be something like this : Stream<Either<JobPostFailure, List<JobPost>>>. What is the equivalent of my above code in Stream?
My try so far :
#override
Stream<Either<JobPostFailure, List<JobPost>>> watchAppliedJobPosts({
required String seamanId,
}) async* {
yield* _firestore
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.snapshots()
.map((event) {
return event.docs.map((e) {
return e.reference.parent.parent!.snapshots().map((event) {
return right(JobPostDto.fromFirestore(event).toDomain());
}).toList();
});
});
}
And its a big mess!
You can use method snapshots instead of get. Is will create a new stream that will fetch data for every change your document or collection has

The method 'data' was called on null

I made this function to get document(post)'s userId but docu always becomes null.
Is there any ideas to solve this problem?
(I use flutter and cloud firestore)
Future<String> getUserId(String text) async{
var docu;
await FirebaseFirestore.instance
.collection('post')
.where('content', isEqualTo: '$text')
.snapshots()
.listen((snapshot){
snapshot.docs.forEach((document) {
docu = document;
print('docUserId : ${document.data()['userId']}'); // this works well
});
});
return docu.data()['userId']; }
Also I tried return only document userId with String like this
Future<String> getUserId(String text) async{
String docUserId;
await FirebaseFirestore.instance
.collection('post')
.where('content', isEqualTo: '$text')
.snapshots()
.listen((snapshot){
snapshot.docs.forEach((document) {
docUserId = document.data()['userId'];
print('docUserId : ${docUserId}'); // this works well
});
});
return docUserId; }
Although print('docUserId : ${docUserId}'); this command works well, the final return value is always null.
I can't find the reason.
You need to take care of 3 importand points here:
You don't need to have a realtime listener if you just want to get the data once
You can't await a listener. Your function will always end before you get the data.
Check if a document exists before you use it
Could you pls try it with this code:
import 'package:cloud_firestore/cloud_firestore.dart';
Future<String> getUserId(String text) async {
String userUid = '';
QuerySnapshot docSnap = await FirebaseFirestore.instance
.collection('post')
.where('content', isEqualTo: '$text')
.get();
docSnap.docs.forEach((DocumentSnapshot document) {
if (document.exists) {
userUid = document.data()['userId'];
print('docUserId : ${document.data()['userId']}');
}
});
return userUid;
}
document.data()
change this to
docu = document;
print('docUserId : ${docu['userId']}');