Reading FirebaseFirestore collection items and saving them to list - firebase

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.

Related

Is it possible to add containers programmatically to a pageview list in flutter?

i wanted my list of pageview containers to be created according to the number of documents in a collection in my cloud Firestore so that when clicked i can display data from each document on each page, is there a way to do this,thanks
You can use a StreamBuilder with a PageView.builder inside as the builder. For example, let Object be the type of the documents you are getting from Firestore:
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: StreamBuilder<List<Object>>(
stream: firestoreService.getObjectsList, // something that returns an Object
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
} else {
return Scrollbar(
child: PageView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
// return something for each object in each page
}),
);
}
}),
));
}

Merging stream in flutter firetore

I am using two quires to fetch data from the firestore.
Query 1.
_firestore
.collection('chats')
.doc(getCurrentUser().uid)
.collection('chatUsers')
.orderBy('timestamp');
with all the querysnapshot document from query 1. I am fetching last message and document id, and displaying the last message in listTile. With document id i am passing the id to fetch other data from other collection like name photo etc.
Query 2.
Future<DocumentSnapshot> fetchUserData(String uid) async => await _firestore
.collection('users')
.doc(uid).get();
So for this I need to use nested stream builder. First stream builder to fetch all data. Second stream builder to fetch user requested data from all data. what will be the best approach?
This is how i am using query 1 in my widgets for the query 2 I have to implement it inside the ListView.builder which will be the nested stream. please guide me with the best approach to this.
SafeArea(
child: Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: _fetchUserChatRoom.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
return _tiles(snapshot.data.docs);
} else if (snapshot.hasError) {
return Icon(Icons.error_outline);
} else {
return CircularProgressIndicator();
}
}),
),
);
}
Widget _tiles(List<QueryDocumentSnapshot> docs) {
return ListView.builder(
itemCount: docs.length,
itemBuilder: (BuildContext context, int index) {
var data = ChatModel.fromMap(docs[index].data());
return GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (_) => ChatScreen(uid: docs[index].id))),
child: ListTile(
leading: CircleAvatar(),
title: Text(data.message),
subtitle: Text(data.timestamp.toString()),
trailing: Text('time'),
),
);
});
You can either use async and await in your ListView.builder, however, I imaging this could slowdown you app and cause a lot of firestore calls.
Widget _tiles(List<QueryDocumentSnapshot> docs) {
return ListView.builder(
itemCount: docs.length,
itemBuilder: (BuildContext context, int index) async {
var data = ChatModel.fromMap(docs[index].data());
var userData = await fetchUserData(data[index].uid);
return GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (_) => ChatScreen(uid: docs[index].id))),
child: ListTile(
leading: CircleAvatar(),
title: Text(data.message),
subtitle: Text(data.timestamp.toString()),
trailing: Text('time'),
),
);
});
Other options (which I use) is to use a Provider class with all the contacts. You can fill the Provider when the app initializes with all the users in your firestore. After that you can use each user data anywhere in your app.

Get only documents in a FireBase collection that have an id matching with documents in a separate collection

I have the following database structure:
users
|_____user01
|_____groups
|_____0002
|_____0004
|_____0006
groups
|_____0001
|_____0002
|_____0003
|_____0004
|_____0005
|_____0006
I currently have a StreamBuilder() that creates a list with all the entries it finds in root/groups, so it creates a list of 6 elements.
I need it to create a list using the entries in root/groups as it's doing now, but only those that have an id that matches the IDs of entries present in root/users/user01/groups, so it should create a list with 3 elements (0002, 0004 and 0006).
I can't wrap my head around how to achieve that.
This is my StreamBuild() :
StreamBuilder(
stream: FirebaseFirestore.instance.collection("groups").snapshots(),
builder: (context, snapshots) {
if (snapshots.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data.documents[index];
return Dismissable(
child: Card(
)
)
Should I make a firebase query using where() that excludes documents IDs not shared by the two collections and feed the results to the StreamBuilder?
I would recommend you to use a FutureBuilder for every document inside the list you are creating using StreamBuilder and rendering using ListView.builder.
Create a future that matches each and every document in the list with documents inside the sub-collection of user i.e, groups.
Future<bool> matchDocs(String id)async{
final result = await FirebaseFirestore.instance.collection('users').doc(FirebaseAuth.instance.currentUser.uid).collection('groups').doc(id).get();
return result.exists;
}
Inside itemBuilder function return a FutureBuilder.
StreamBuilder(
stream:
FirebaseFirestore.instance.collection("groups").snapshots(),
builder: (context, snapshots) {
if (snapshots.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data.documents[index];
return FutureBuilder<bool>(
future: matchDocs(
snapshots.data.documents[index].data()['id'].toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data) {
return Dismissible(key: null, child: null);
}
}
return Container();
}
);
}
);
}
}
)
Note: I did not have any idea about your exact data models, so you
should be careful while implementing this solution.

Correct use of Streams with Flutter-Listview

I am trying to display a realtime chat-screen in flutter with with firebase-firestore (equal to the homescreen of whatsapp).
Working: Creating a list of all the contacts "peers". Have a Look at my Listview:
Container(
child: StreamBuilder(
stream:
//FirebaseFirestore.instance.collection('users').snapshots(),
FirebaseFirestore.instance
.collection('users')
.doc(currentUserId)
.collection('peers')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
);
} else {
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) =>
buildItem(context, snapshot.data.documents[index]),
itemCount: snapshot.data.documents.length,
);
}
},
),
),
not working: Loading specific data for each tile like last message or name. I cant query this at the time of creating my first list (first query returns peer-ids, second returns userdata of a peer-id). My buildItem method consists of another streambuilder, however, as soon as the first streambuilder makes changes, the app freezes.
Widget buildItem(BuildContext context, DocumentSnapshot document) {
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(document.data()['peerId'])
.snapshots(),
builder: ...
Is this the proper way to nest streams? Simple Listviews are documented quite well, but i couldn't find a good example on this on google. Any help is appreciated.
Try creating your stream just once in initState and pass it onto this method:
//in initState
peersStream = FirebaseFirestore.instance
.collection('users')
.doc(currentUserId)
.collection('peers')
.snapshots(),
Then use stream: peersStream in the StreamBuilder.
Also, it is recommended to use widget-classes over methods for widgets: https://stackoverflow.com/a/53234826/5066615

Firebase - get firestore Array Data to StreamBuilder Flutter

child: StreamBuilder(
stream: databaseReference
.collection(collectionName)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView(
shrinkWrap: true,
children: elementList(snapshot),
);
}
}
),
this is the my StreamBuilder code,
elementList(AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.data.documents.map((document).mydata {
return ListTile()
}).toList();
}
mydata is the document name like Country, and name of array List,
this is the ListTile building code, and I want get the Country list in this StreamBuilder, and elements to ListTile.
the database looks like,
FireStore database Country List in the document in the collection

Resources