Possible to get metadata from Firestore snapshot Flutter? - firebase

I need to get snapshot metadata so can check if write to Firestore successful. I look at source and see there is SnapshotMetadata and boolean hasPendingWrites(). But I cannot find how to implement. No open source dart project have used it.
Firebase doc say can use: .onSnapshot / .addSnapshotListenerto specify includeMetadataChanges: true.
But I need to make sure I get metadata when making a query for QuerySnapshot. I am use query for stream not addSnapshotListener.
Like this:
child: new FirestoreAnimatedList(
query: Firestore.instance.collection('Collection')
.orderBy('timestamp', descending: true)
.snapshots(),
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, DocumentSnapshot snapshot,
Animation<double> animation, int x) {
return new Chat(
snapshot: snapshot, animation: animation);
},
),
I have try specify:
query: Firestore.instance.collection('Collection')
.snapshots(includeMetadataChanges: true),
But this not possible:
error: The named parameter 'includeMetadataChanges' isn't defined.
I also try:
snapshot.getMetadata().hasPendingWrites()
But give error:
error: The method 'getMetaData' isn't defined for the class
'DocumentSnapshot'.
Does anyone know how do this in Flutter? Is possible?
I try so long but cannot find how.. Help!
Thanks!

includeMetadataChanges Parameter Added
Support in Flutter for the includeMetadataChanges parameter was added in the cloud_firestore package at version 0.12.9.
When you are calling your snapshots() function, you can now include it as a parameter.
This example returns a Stream of all documents in a collection, as a list of contacts. If includeMetadataChanges is false (default behavior) then the stream will not be updated when metadata changes (such as hasPendingWrites or isFromCache). If true, then the stream is updated by these changes.
Stream<List<Contact>> getAllContactsStream() {
return Firestore.instance.collection('contacts')
.orderBy('name', descending: false)
.snapshots(includeMetadataChanges: true)
.map((snapshot) => snapshot.documents.map((document) => Contact.fromFirestore(document)).toList())
}
With a single document snapshot, normal Firestore data is accessed with document.data. The metadata is accessed with document.metadata.

It looks like the DocumentSnapshot class in FlutterFire doesn't expose the metadata of the underlying document. I'd file a feature request for it on the Flutter repo.

Related

Flutter Firestore - How to get data from a Document Reference in a Document Field?

I'm building a Self-learning app with differente questions types. Right now, one of the questions have a field containing a list of DocumentReferences:
In Flutter, I have the following code:
Query<Map<String, dynamic>> questionsRef = firestore
.collection('questions')
.where('lesson_id', isEqualTo: lessonId);
await questionsRef.get().then((snapshot) {
snapshot.docs.forEach((document) {
var questionTemp;
switch (document.data()['question_type']) {
....
case 'cards':
questionTemp = CardsQuestionModel.fromJson(document.data());
break;
....
}
questionTemp.id = document.id;
questions.add(questionTemp);
});
});
Now, with "questionTemp" I can access all the fields (lesson_id,options,question_type, etc..), but when it comes to the "cards" field, how Can I access the data from that document reference?
Is there a way to tell firestore.instance to get the data from those references automatically? Or do I need to make a new call for each one? and, if so, how can I do that?
Thank you for your support in advance!
Is there a way to tell firestore.instance to get the data from those
references automatically? Or do I need to make a new call for each
one?
No there isn't any way to get these documents automatically. You need to build, for each array element, the corresponding DocumentReference and fetch the document.
To build the reference, use the doc() method
DocumentReference docRef = FirebaseFirestore.instance.doc("cards/WzU...");
and then use the get() method on this DocumentReference.
docRef
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
}
});
Concretely, you can loop over the cards Array and pass all the Futures returned by the get() method to the wait() method which "waits for multiple futures to complete and collects their results". See this SO answer for more details and also note that "the value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating futures."

How to filter sub collection in firebase (flutter)?

I have a collection called Channels and inside it I have a sub collection called messages, so I want to filter the channel collection then I want to fetch the messages from it ?
FirebaseFirestore.instance
.collection('Channels') >> where(id = user.uid) <<<<< I want to filter this
.doc()
.collection("messages")
.orderBy("createdAt")
.snapshots(),
*any help will be appreciated ^^
error :
The method 'doc' isn't defined for the type 'Query'.
Try correcting the name to the name of an existing method, or defining a method named 'doc'.
According to this link you should use the where method. Combine that with firebase.flutter.dev docs (heading with relevant example here), you should be able to accomplish this using something like:
FirebaseFirestore.instance
.collection('Channels')
.where('id', isEqualTo: user.uid)
Also is channelID same with senderID? If the answer is yes, do you need both (and are you sure that is the structure you want)? :)
A Firestore query can only access documents from a single collection, or from a group of collections all with the same name. So you can't filter documents from messages based on fields in their parent Channels document.
What you can do is add the relevant field to each messages document, e.g. in a field named channelUID. Then you can use a collection group query to query all messages collections:
FirebaseFirestore.instance
.collectionGroup("messages")
.orderBy("createdAt")
.where("channelUID", isEqualTo: user.uid)
.snapshots(),
The problem is that the doc() is empty, so FB does not know which doc to go into.
Just add the id of the channel inside the doc(), like so:
FirebaseFirestore.instance
.collection('Channels')
.doc(recieverID)
.collection('messages')
.orderBy('createdAt')

The argument type 'User?' can't be assigned to the parameter type 'Future<Object?>?'

With reference to the recent changes regarding FirebaseUser to User. FirebaseAuth.instance.currentUser() is not found at all (while throughing error "The expression doesn't evaluate to a function, so it can't be invoked." The solution to that however was to simply remove the paranthesis as FirebaseAuth.instance.currentUser. But now we have another error that it isn't a future type i.e "The argument type User can't be assigned to the parameter type Future<Object?>?". Following is my code block.
return FutureBuilder(
future: FirebaseAuth.instance.currentUser,
builder: (ctx, futureSnapshot) => ListView.builder(
reverse: true, // So that 1st message goes to last.
itemCount: chatDocument.length,
itemBuilder: (ctx, index) => MessageBubble(
message: chatDocument[index]['text'],
isMe: chatDocument[index]['userId'],
),
),
);
In the above code block I intend to provide future to my `FutureBuilder`. In summary previously `FirebaseUser` object did return a future, but probably `User` doesn't anymore. How may I handle this case? Thanks in advance.
I don't typically do that with a FutureBuilder. Once you have a user, you don't need to async it.
final FirebaseAuth_auth = FirebaseAuth.instance();
final User? user;
user = _auth.currentUser;
Then, if user != null ....create your ListView else, return a CircularProgressIndicator or whatever.
Look up Net Ninja for some nice videos for getting yourself set up with all that, just having a stream based on userChanges() for your project. More robust setup.

How can I order a cloud firestore snapshot with 2 fields?

I'm trying to order a cloud firestore snapshot with 2 fields. I'm using flutter/dart
I have this error :
NoSuchMethodError: The getter 'lenght' was called on null. Receiver: null. Tried calling: length. See also: https//flutter.dev/docs/testing/errors.
I'm using cloud_firestore: ^2.2.0
My code:
Stream<List<Friend>> getFriends() {
return _db
.collection('profile')
.doc(userId)
.collection('friends')
.orderBy('status', descending: true)
.orderBy('displayName', descending: false)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => Friend.fromJason(doc.data()))
.toList());
}
Thank you for your help!
The type of query you do (ordering on two fields) is supported on Firestore, but does require an index that is not automatically created.
If you check the debug output of your app, you should find another error message when this query runs. In this error message there is a link that takes you to the Firebase console with all the information already filled in. From there it takes a single click to create the index, and make the query work.
Just Add Your Index Query. Here is the Example : Please Check . it will take 2-5 minits to Build the index query . after that you will be able to see like this .
Note : If you use VS Code , go to Debug/Terminal You will find out the direct link via clicking this link indexs query will auto generate for you . link will redirect you to the Firestore indexes console

Get all documents from collection in firestore

I'm trying to get all posts from my 'instagram' clone app. This is the path I have in firestore: posts > (unique ownerId) > userPosts > (unique postId)
How can I retrieve all posts using a stream builder? I tried doing so with
body:
StreamBuilder<QuerySnapshot>(
stream: postsRef.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<Post> posts = snapshot.data.documents.map((doc) => Post.fromDocument(doc)).toList();
return ListView(children: posts);
},
)
I want to display only the posts' pictures in a stack of cards.
You can't use wildcards with listeners in Cloud Firestore. You need to name the specific document and collections in the path. So, if your (unique ownerId) is unknown at the time of the query, you will not be able to know anything about the documents changing in its subcollections.
As an alternative, on a backend you control, you can list subcollections of a document, then query those documents. Or, you can use a Cloud Functions trigger and notify interested client apps (maybe with Cloud Messaging) as changes happen.

Resources