Why is my builder function executed twice? - firebase

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.

Related

How to show real time data from from realtime database?

I'm making this realy simple project where when you click a button, it increments a value in the realtime databse. This works, but I want a Text widget to update everytime the value changes. I've really new to this so please excuse my lack of knowledge. I have noticed that there's no snapshots methods available that would let me do something like this, so how I update a label to be the data in the realtime storage eveyime it changes? Here's the code which I use to increment the values-
Future<void> addData(int id) async {
DatabaseReference pointsRef = FirebaseDatabase.instance.ref("$id");
DataSnapshot snapshot = await pointsRef.get();
pointsRef.set((snapshot.value as int) + 1);
}
Use StreanBuilder like so:
StreamBuilder(
stream: FirebaseDatabase.instance.ref("id").onValue,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return Text(snapshot.data.toString());
},
)

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

StreamBuilder returns nothing in the snapshot

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

Flutter Future:A build function returned null

I want to retrieve data from firebase to future builder.I use this function for that.
Future getAllData() async {
ReseviorDataModel resModel;
DatabaseReference ref = FirebaseDatabase.instance.reference();
DataSnapshot childed =await
ref.child("Reservoir/${widget.placeName}").once();
Map<dynamic, dynamic> values;
values = childed.value;
resModel = ReseviorDataModel.customConstrcutor(values["sensor1"],
values["sensor2"], values["sensor3"]);
return resModel;
}
I invoke this function inside my future builder.
child: FutureBuilder(
future: getAllData(),
builder: (context, snapshot) {
Center(child: Text(snapshot.data));
}
but it keeps throwing this "A build function returned null." error .i cant figure out what is the problem here
To clear things up here, your builder always has to return a widget to display. You can never return null, as the error message describes.
The problem in this case was that the OP didn't return anything from the builder, so just adding a return worked out fine.
Some things to keep in mind when using FutureBuilder. Always check the snapshot.hasData property and return a UI accordingly. This will prevent cases where the snapshot.data is null causing a new error to be thrown in your Widget taking in the null.
Example:
child: FutureBuilder(
future: getAllData(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
// show loading while waiting for real data
return CircularProgressIndicator();
}
return Center(child: Text(snapshot.data));
}

Flutter Firestore Ordering Issues

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

Resources