Manually add item to flutter Stram builder in flutter - firebase

I have made flutter streambuilder that creates a flutter listview from results returned from a query.
StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
scrollDirection: Axis.horizontal,
children: snapshot.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
addChildBtn();
return ListCard(
firstName: data['childFirstName'],
lastName: data['childLastName'],
points: data['points'],
);
},
).toList(),
);
},
),
It returns a listCard, a custom widget I made which consists of a container. However, I want to have a container always in the listview, regardless if there any items returned from the steam or not.

I have the same exact question. did you find an intelegent away for that?
my workaround which is not super, I added one document in firestore with specfic name, then in the list view builder I check for the name, if matched I return my fixed customized item, if doesn't match then I return the regular card.
the result is what you want, the constrain is that if you have different categories in your firestore database, then you will need to add this specific document everywhere.

Related

how to retrieve single field data of type number from firebase in text widget flutter

I just want to retrieve a field from firebase firestore document which is <String, dynamic>, please tell me whats wrong with the code.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('qazanamazcalc')
.snapshots(),
builder: (ctx, AsyncSnapshot snap) {
return Text(
snap.data.docs['countcalc'],
// snapshot.data['countcalc'],
);
},
),
I am getting these two errors
'docs' method not found Receiver: null Arguments: []
and
Expected a value of type 'int', but got one of type 'String'
First of all, are you using a stream, which is a continuous data stream that listens to changes in a collection or a specific document. If I understand your question correctly you just want to do one fetch on a specific document.
FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
future: FirebaseFirestore.instance.collection('qazanamazcalc').doc('doc_id').get(),
builder: (_, snapshot) {
if (snapshot.hasData) {
var data = snapshot.data!.data();
var value = data!['countcalc'];
return Text(value);
}
return Center(child: CircularProgressIndicator());
},
)

Reading FirebaseFirestore collection items and saving them to list

I am having a trouble reading collection from firebase and saving values in a list.
I basically have a collection called 'brands' where I have car brands like this:
Firebase 'brands' collection screenshot
I need these car brands to be saved as a list like this, to be able to use it in a dropdown menu as items:
<String>[
'ferrari',
'mercedes',
'porsche',
]
I have tried using StreamBuilder (below) but it requires to return a widget and I do not actually need a widget to be returned, so below StreamBuilder is just an experiment "in progress".
Do you have any ideas?
final stream = FirebaseFirestore.instance
.collection('accounts')
.doc('dealers')
.collection(user!.uid)
.doc(dealerName)
.collection('brands')
.snapshots();
StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Text('Error in receiving snapshot: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
);
}
return ListView.builder(
padding: EdgeInsets.all(8),
reverse: true,
itemCount: snapshot.data.docs!.length,
itemBuilder: (BuildContext context, int index) {
return Text(
snapshot.data.docs[index]['brandName'],
);
},
);
},
);
Once you get the data from firebase, loop through it and add the car brands to your list. Try this:
List<String> myBrands = [];
final dataRef = await FirebaseFirestore.instance
.collection('accounts')
.doc('dealers')
.collection(user!.uid)
.doc(dealerName)
.collection('brands')
.get();
dataRef.docs.forEach((doc) {
myBrands.add(doc.data()['brandName']);
});
You should then be able to use the myBrands list for your dropdown menu.

Flutter Listview duplicates random element instead of adding new element on new data

I'am trying to create something like feed with Flutter and Firebase and I want to order posts by their 'postedAt' value but the problem is when I use collection.orderBy() it is not working properly. It adds new post to the random place at the feed and gives it a imageURL of random existing post (or I think it just duplicates one item in the feed). I created indexes at Firebase I tried both index types none working. The strange thing is, when I'm adding new post from the phone when my adding completed it just works fine but when listing on the other phone it shows described behaviour.
Edit: when I hot reload it works correct (last added post shows up on the top)
Edit: when I removed the orderBy method problem continued.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser.uid)
.collection("posts")
.orderBy('postedAt', descending:true)
.snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
print("error:" + snapshot.error.toString());
}
List<Post> posts = [];
posts = snapshot.data.docs.map((postDoc) {
return Post.fromDoc(postDoc);
}).toList();
if (posts.isEmpty) {
return Center(
child: Text("No posts"),
);
}
return ListView.builder(
cacheExtent: 4000,
itemCount: posts.length,
itemBuilder: (BuildContext context, int index) {
return PostWidget(
post: posts[index],
);
},
);
},
)
my indexes:
Composite index
single index
screenshot of the document:
document ss
I solved this issue.
I thought that if it is duplicating the elements, may be it cannot seperate them. and added key paramater to PostWidget so it worked.

Nesting two stream builders causing bad state error

I am fetching data from two different firestore collections and this is my code
StreamBuilder(
stream: Firestore.instance.collection('items').snapshots(),
builder: (BuildContext context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return CupertinoActivityIndicator();
}
if(snapshot.data != null){
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context,index){
return Column(
children: <Widget>[
Text(snapshot.data.documents[index]['name']),
Text(snapshot.data.documents[index]['color']),
Text(snapshot.data.documents[index]['lifetime']),
Container(
child: StreamBuilder(
stream: Firestore.instance.collection('users')
.document(userid).collection('Quantity')
.document(snapshot.data.documents[index]['id']).snapshots(),
builder: (BuildContext context, snap){
if(snapshot.connectionState == ConnectionState.waiting){
return CupertinoActivityIndicator();
}
if(snap.data != null){
return Container(
child: Text(snap.data.documents.length)
);
}
},
),
)
],
);
});
}
},
)
It is giving me error but when I use futurebuilder inside streambuilder everything works fine and I also used stream broadcast but it is also giving me same error.
Here is the code which I used for broadcast stream
StreamController _controller = StreamController.broadcast();
Stream getItems() async*{
Firestore.instance.collection('items').snapshots().listen((data){
_controller.add(data);
})
yield* _controller.stream;
}
You shouldn't create a new Stream inside the StreamBuilder. When you do:
StreamBuilder(
stream: Firestore.instance.collection('items').snapshots(),
And
StreamBuilder(
stream: Firestore.instance.collection('users')
.document(userid).collection('Quantity')
.document(snapshot.data.documents[index]['id']).snapshots(),
Each time your build() function is called a new StreamBuilder is created, so Firestore.instance.collection()...snapshots() is called, returning a new Stream each time.
You should convert your widget to a StatefulWidget and initialize your Stream on initState(), passing it as a class variable to your StreamBuilder. The nested StreamBuilder can also be transformed into a StatefulWidget and created in place, but initialized on the same manner. Just pay attention that you might need a Key for showing it correctly on a ListView.
Also if you want to convert a Single Subscription Stream to a Broadcast Stream you just have to call asBroadcastStream to convert it.

A simple where clause in flutter/firebase database

Today I started experimenting with Firebase Live database. Maybe I'm thinking too much in sql terms. But what I want to do is get a record from my database where a value equals a variable with flutter. My table looks like this:
What I'm trying to achieve is something like this:
FirebaseDatabase.instance.reference().child('users').where('User_id', 1508)
Like I said. I'm a complete beginner when it comes to Live Databases.
I hope someone can help me with this issue I'm having.
Kind regards,
Namanix
According to firstore docs
Firestore.instance
.collection('talks')
.where("topic", isEqualTo: "flutter")
.snapshots()
.listen((data) =>
data.documents.forEach((doc) => print(doc["title"])));
If you have the user id in a variable for example called:
String uid = currentUser.uid;
then you can do the following:
FirebaseDatabase.instance.reference().child('users/$uid')
Update
I think this is what you are asking about
FirebaseDatabase.instance
.reference().child("users")
.orderByChild("User_id")
.equalTo($this.userId)
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore
.instance
.collection('users')
.where("user_id", isEqualTo: FirebaseAuth.instance.currentUser!.uid)// 👈 Your where condition here
.get(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('Error = ${snapshot.error}');
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['avatar']), // 👈 Your valid data here
);
}).toList());
},
)),
);
}
Also refer: How to use StreamBuilder and FutureBuilder for single and multiple documents

Resources