Flutter Firestore Ordering Issues - firebase

I'm new to Flutter so please bear with me. So I need to fetch a User object from Firestore immediately once I hit the home page. The problem is that currently, I am attempting to fetch the User in the initState() function and the build function (which needs the user object) is triggered before the User object is fetched, causing an exception.
Here is my User fetching code in initState()
//get user object
StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection("users")
.document(widget.uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
setState(() {
user = snapshot.data.data as User;
});
},
);
ALSO, I just figured out that this code doesn't ever get to the setState block. Don't know why. I know the stream Firestore ref is correct. Any recommendations on how to re-organize my code? Thanks!

use stream builder as a widget immediately inside builds function.
StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection("users")
.document(widget.uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
return Text(snapshot.data.data.userName); // example
},
);

Related

retrieving current user to show in personal profile

hi im trying to retrive current user info from firebase but its showing me errors this error
this is my code
final auth = FirebaseAuth.instance;
final db = FirebaseFirestore.instance;
User? user = FirebaseAuth.instance.currentUser;
Padding(
padding: EdgeInsets.only(left: 20, right: 20),
child: StreamBuilder(
stream: db
.collection("Users")
.doc("list_students")
.collection("Students")
.doc(user!.uid)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center();
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Stack;
this is a screenshots of my firebase
First of all, try to print 'snapshot' and 'snapshot.data' for you've got proper data from firebase.
You need to cast snapshot.data as a List type before you can call the length method on snapshot.data. I cannot tell from your included code what is the underlying type for snapshot.data.
Another way is to specify AsyncSnapshot<List<String>> snapshot in the builder header, assuming the underlying data type is a List<String>.

Flutter does popping a screen with a firestore stream reread the document

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());
}

Error getting data from subcollection of flutter firebase

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");
},
);

Why is my builder function executed twice?

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.

Firebase in flutter getCurrentUser never unwraps the Future and I can't get at the Data

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();
}
},
);

Resources