In a flutter messaging app, chatrooms are created and on the conversation screen, I can access the subcollection of the messages. But when the same subcollection I am trying to access on the main page
(where existing chats are shown) I cannot access them.
I have a collection of ChatRooms, in which users as an array are stored. Then, Messages named subcollection stores the messages.
See, the document is named lexicographically attached with and in between. Further, it has Messages collection.
And messages collection is also not empty.
On the main page, the existing chats are shown in listTile. I want to show the last message in its subtitle.
So, here is the last message stateful widget.
class _LastMessageState extends State<LastMessage> {
#override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection("ChatRooms")
.doc(widget.chatRoomID)
.collection("Messages")
.orderBy("Time")
.snapshots()
.last,
builder: (context, documentSnapshot) {
return Text(documentSnapshot.data.docs.last.get("Message"));
});
}
}
Always the bad state error is coming up.
I would be glad if you could figure out the problem.
Edit :
This is my firestore rules.
You should use the limit() method, in order to get a QuerySnapshot with only one document. Then you can do as follows, knowing the first (and unique) element of the docs list is the document you are looking for:
return FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance.
.collection("ChatRooms")
.doc(widget.chatRoomID)
.collection("Messages")
.orderBy("Time", descending: true)
.limit(1)
.get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("...");
}
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.size > 0) {
return Text(snapshot.data.docs[0].get("Message"));
} else {
return Text("No document");
}
}
return Text("Loading");
},
);
Related
I am fairly new on flutter and firebase, i am currently developing app which have a streambuilder with stream from a firestore snapshot, the statefulwidget class is more or less like this :
StreamBuilder(
stream: ItemService().getItemsWithStatus([1]),
builder: (context, AsyncSnapshot<List<Item>> snapshot) {
//Widget to show information
}
)
and i get the stream from itemService class like this
class ItemService{
Stream<List<Item>> getItemsWithStatus(List<int> status) {
return _firestore
.collection(itemsPath)
.where('status', whereIn: status)
.snapshots()
.map((QuerySnapshot snapshot) => snapshot.docs
.map((DocumentSnapshot document) => Item.fromDb(document.data()))
.toList());
}
}
the question is , when i close the screen with that streambuilder and then reopen it would it read the data again from firestore (and doubling my read count)? if yes then how can i possibly do to avoid reread?
Thankyou, any advice will really be appreciated
Probably no. You can check with this alteration:
(DocumentSnapshot document) {
print(document.metadata.isFromCache);
Item.fromDb(document.data());
}
I want to check whether collection with this username and account type exists, it means I want to see if user is premium.
The output when app runs is:
ok
user
ok
model
Why does it print 'ok' twice and it looks like snapshot both has and hasn't any data?
Here is part of the code, if it doesn't say anything I will provide full class:
#override
Widget build(BuildContext context) {
return Scaffold(
body: isLoading
? Container(
child: Center(child: CircularProgressIndicator()),
)
: StreamBuilder(
stream: Firestore.instance
.collection('users')
.where('email', isEqualTo: email)
.where('account', isEqualTo: 'model')
.snapshots(),
builder: (context, snapshot) {
print('ok');
if (!snapshot.hasData) {
box.put('account', 'user');
print(box.get('account'));
} else {
box.put('account', 'model');
print(box.get('account'));
}
return Container(...
Thank you in advance and maybe there is easiest way to see if collection with such data exists?
As far as I can see this is working as intended. When your widget is first rendered, it starts loading the data for the stream from Firestore. At that point snapshot.hasData is still false, so it renders your widget with the if block.
Then when the data becomes available, the stream gets updated, and that triggers the widget to be rendered again. At this point, the snapshot.hasData is true, so it renders your widget with the else block.
I have set a stream from database that I created manually on firebase with some sample fields, when I use streamBuilder inside a stateful widget the snapshot.data return nothing/null.
final CollectionReference **storeCollection** = Firestore.instance.collection('stores');
Stream**<List<Stores>>** get **stores** {
return **storeCollection.snapshots()**.map(_storeListFromSnapshot);
Then after I used a StreamBuilder to get snapshot.data but it returns null
#override
Widget build(BuildContext context) {
return **StreamBuilder**<Object>(
stream: DatabaseService().**stores**,
builder: (context, **snapshot**) {
**List<Stores> stores** = **snapshot.data** ?? []; //returns null on this line
I was able to update data to firebase with storeCollection.document(uid).setData()
Usually an AsyncSnapshot is received at the beginning of the listening to signal that the subscription is active and it's waiting for something and it does not contain data.
You can find more checking out the docs about the ConnectionState enum which denotes the state of an AsyncSnapshot.
That said, the most basic thing you can do with the builder of your StreamBuilder is this:
builder: (context, snapshot){
if (snapshot.hasData){
List<Stores> stores = snapshot.data;
//the rest of your logic on stores
}else{ //nothing yet, we're waiting for the event, show a loader or whatever
return Center(child: CircularProgressIndicator());
}
}
So I have a Flutter app with Firebase authentication. I have a dart file for sign in and registration and then a separate dart file with different Widgets for a list of data. I want to get the current user so I can get that users personalized data from the database. But for some reason I can never get that user. Here is the code:
Future getUser() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.getIdToken();
}
return FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if (snapshot.hasData) return Text(snapshot.data);
else if (snapshot.hasError) return Text("data error (see futurebuilder)");
return Text("Await for data");
},
);
But on my debugging device the Text output just says data error (see future builder) which is the error message I wrote. However I can't figure out what I'm doing wrong. I don't see why this isn't just giving me the user ID token. Thanks.
Try changing your code from snapshot.hasData to snapshot.connectionState == ConnectionState.done and see if this helps you. I have the exact same setup in my app and this works for me.
something like this should work
FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return
//Execute code after future is returned
} else {
return CircularProgressIndicator();
}
},
);
I am new to firebase/firestore and wanted to add firestore to my app. My app currently has a login and adds data to the database with the UID set as the document name. Console Image
I want to display the name in my apps profile page. How would I achieve this?
Called it with this
Center(child:building(context),),
Widget building(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance
.collection('UserData')
.document(getUID())
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
} else {
return new Text(snapshot.data.toString());
}
});
}
Current Error
Error Image
Previous Error
Error Message
Thanks in advance!
Try this
Widget building(BuildContext context) {
return new StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('UserData')
.document('TjMJDFd940UtLORgdYND771GYwG2')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
} else {
Map<String, dynamic> documentFields = snapshot.data.data;
return Text(documentFields["First Name"] +
" " +
documentFields["Last Name"]);
}
});
}
Note that TjMJDFd940UtLORgdYND771GYwG2 refer to documentID.
The docs for the Flutter Firebase API are stashed away and are admittedly hard to find. Here's the documentation on the QuerySnapshot class.
You are trying to look at the .document property of a QuerySnapshot object, so it's throwing an error because that property does not exist. Try using snapshot.documents to get a list of documents to iterate over (or just use snapshot.documents[0] if there will always only be one), and then read the data from that, i.e.: snapshot.documents[0].data[documentId]['First Name']. I removed quotes from documentId reference, since we want to index the variable value and not just the string "documentId".