Flutter: get a list of maps when using Firebase snapshots - flutter

This is my stream function:
Stream<QuerySnapshot> getPortfolios() {
return db
.collection('users')
.doc(authService.getUser().uid)
.collection('portfolios')
.snapshots();
}
In the StreamBuilder I get the list like this:
portfolios = snapshot.data!.docs;
That gives my a list but a list of JsonQueryDocumentSnapshot. With each item I can do .data() and I get the info I need, but how could I get this map directly in the list without calling extra methods?
I tried this old answer but it doesn't work anymore:
final QuerySnapshot<Object?>? ds = snapshot.data;
final Map<String, dynamic> map = ds!.data; // this .data is not recognised

You can use ".docs" on the snapshot to get a list of QueryDocumentSnapshot which have some of the same properties as a map, so you can directly reference a value like im doing below:
List<QueryDocumentSnapshot> data = snapshot.data!.docs;
String name = data[i]['name']
Here is a full example :
StreamBuilder<QuerySnapshot>(
stream: db.collection('users').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
List<QueryDocumentSnapshot> data = snapshot.data!.docs;
return ListView.builder(
itemCount: snapshot.data!.size,
itemBuilder: (context, i) {
return Text('${data[i]['name']}');
},
);
} else {
return CircularProgressIndicator();
}
},
),
Alternatively you can map the Stream to any object like this:
Stream<QuerySnapshot<Map<String, dynamic>>> snaphot = db
.collection('users')
.doc(authService.getUser().uid)
.collection('portfolios')
.snapshots();
//Map to an object (note that you need to create a "fromJson" method for your object.
Stream<List<YourObject>> dataStream = snaphot.map((list) => list.docs.map((doc) => YourObject.fromJson(doc.data())).toList());
Now you can use the "dataStream" in your stream builder and directly reference the items in the list with "snapshot.data".
// snapshot.data is now the type:
List<YourObject>

Related

Flutter error : Failure to access data inside a snapshot fetched from Firebase Firestore

Using Flutter 3.3.9, I fetch a record from my Firestore database using a Streambuilder. I use the following code segment to do this:
StreamBuilder<Object>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(userId)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Loading...');
}
return Text(
snapshot.data!.doc['username'], // This line is marked as error bexcause "doc" is illegal.
),
);
},
),
The snapshot.data!.doc['username'] gives the following error:
The getter 'doc' isn't defined for the type 'Object'.
I verified that the 'Object' is of type "AsyncSnapshot" (=snapshot.runtimeType). It looks like the only getters available for the snapshot.data are hashCode, runtimeType, toString(), and noSuchMethod(..).
I tried
snapshot.data!().doc['username'],
But this does not work either. The error is "The expression doesn't evaluate to a function, so it can't be invoked"
I was able to access the data without using the StreamBuilder. The following works:
final docRef = FirebaseFirestore.instance
.collection('users')
.doc(userId);
docRef.get().then(
(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>;
print(data['username']);
},
onError: (e) => print("Error getting document: $e"),
);
you have two mistakes, in your piece of code, you should specify the type of the AsyncSnapshot, like this:
StreamBuilder<DocumentSnapshot>( // Specified type
stream: FirebaseFirestore.instance
.collection('users')
.doc(userId)
.snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) { //Specified type
//...
now using snapshot.data!, it should be a DocumentSnapshot type, and as I see, that you're trying to get the data of that document so you need also to change this line:
snapshot.data!.doc['username'],
to this:
(snapshot.data!.data() as Map<String, dynamic>)['username'],
now it will access the username field properly.
You define your StreamBuilder's type in wrong way, change it to this:
StreamBuilder<AsyncSnapshot>(
...
)

firestore doesnt show documents even though they are available

I have following code to add data to firebasefirestore
Future<void> sendMessage({
required String msg,
required String id,
}) async {
var docId = getDocId(id); // returns sth like "AbcDe-FghiJ"
DocumentReference documentReferencer = chat.doc(docId).collection('chatMsg').doc();
Map<String, dynamic> data = <String, dynamic>{
"message": msg,
"sentBy": ownId,
"sentAt": DateFormat('yyyy-MM-dd – kk:mm:ss').format(DateTime.now())
};
await documentReferencer.set(data);
}
I used following code to get the data
StreamBuilder<QuerySnapshot>(
stream: firebaseInstance.collection('Messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return const Center(
child: CircularProgressIndicator()
);
} else {
var data = snapshot.data.docs;
return listBuilder(data);
}
}
)
listBuilder(listData) {
return ListView.builder(
shrinkWrap: true,
itemCount: listData.length,
itemBuilder: (BuildContext context, int index) {
return Text(listData[index].id);
}
)
}
However, data show 0 items even though there is a document present.
My question is how can I get the list of documents from Messages?
I was having the same exact problem with subcollections on Firestore and even asked a question here to get some help over it. Though, it seems like the snapshots won't show the documents having a subcollection in them as there is no field inside any of them. So what I did to counter this was to just add anything (just a random variable) and then it was able to find the documents.
This is my current layout:
I've just added another line of code to just add this whenever I'm inserting a new subcollection.
collection
.set({
'dummy': 'data'
})
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));

firestore the getter 'length' isn't defined for the type 'DocumentSnapshot<Object?>'

I try to count all docs from one user in firestore.
My code:
Widget booksWidget(String userData) {
return
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("bookList")
.doc(userData)
.collection(userData)
.orderBy("timestamp", descending: true)
.snapshots(),
builder: (BuildContext context,AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var userDocument = snapshot.data as DocumentSnapshot?;
String books = userDocument?.length.toString();
return Text(books);
}else{
return Text("none");
}
}
);
}
the error:
The getter 'length' isn't defined for the type 'DocumentSnapshot<Object?>'.
thanks for help, streambuilder after migration to null-safety is quite different :(
You're requesting the snapshots of a query, so the snapshot.data that you get is going to be of type QuerySnapshot (and not a DocumentSnapshot? as you assume now).
if (snapshot.hasData) {
var querySnapshot = snapshot.data! as QuerySnapshot;
String books = querySnapshot.docs.length.toString();
...
In cases like this I find it easiest to work from the reference documentation, such as the one for Query.snapshots() here.

How to order Firestore documents in list form based on int values in flutter

I currently have a list of documents that each contain an int value called plastics. Currently, the list only displays the documents in order by when it was added to the collection, but I want to be able to order the documents based on the int value within each one. I've looked around on the web and I've only found tutorials mostly on ordering timestamps. Is there any documentation or sources on this matter? Here is the code situation I'm working with:
Firstly, in my app users can join groups, and when they do so they bring along their name and int data which is then stored in documents for each user.
Future<String> joinGroup(String groupId, String userUid, String displayName,
String plastics) async {
String retVal = 'error';
List<String> members = List();
try {
members.add(displayName);
await _firestore.collection('Groups').doc(groupId).update({
'members': FieldValue.arrayUnion(members),
});
final uid = FirebaseAuth.instance.currentUser.uid;
await _firestore.collection('UserNames').doc(uid).update({
'groupId': groupId,
});
//Below me is the code for doing so
await _firestore
.collection("Groups")
.doc(groupId)
.collection("Members")
.doc(userUid)
.set({'displayName': displayName, 'plastics': plastics});
retVal = 'success';
} catch (e) {}
return retVal;
}
I then take that code access the documents and put them in a list.
#override
Widget build(BuildContext context) {
final CollectionReference users = firestore.collection('UserNames');
final String uid = auth.currentUser.uid;
return FutureBuilder(
future: users.doc(uid).get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final result = snapshot.data;
final groupId = result.data()['groupId'];
return FutureBuilder<QuerySnapshot>(
// <2> Pass `Future<QuerySnapshot>` to future
future: FirebaseFirestore.instance
.collection('Groups')
.doc(groupId)
.collection('Members')
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
final List<DocumentSnapshot> documents = snapshot.data.docs;
return ListView(
children: documents
.map((doc) => Card(
child: ListTile(
title: Text(doc['displayName']),
subtitle: Text(doc['plastics'].toString()),
),
))
.toList());
} else if (snapshot.hasError) {
return Text('Its Error!');
}
});
}
});
}
Is there a specific function needed so that the documents in the Member collection are listed based on the numerical value of the plastics?
You can use orderBy to sort your results.
FirebaseFirestore.instance
.collection('Groups')
.doc(groupId)
.collection('Members')
.orderBy('plastics', descending: true)
.get()

Move method to streambuilder

List<Photo> imgList = [];
Future getCarouselWidget() async {
var firestore = Firestore.instance;
QuerySnapshot qn =
await firestore.collection("history").getDocuments();
List pics = qn.documents
.map((it) => Photo(it['photo'].toString(), it['name'].toString(), it['address'].toString()))
.toList();
return imgList = pics;
}
Hi all, when I make changes in my DB I dont see them in my app? can anybody help me or guid me how to wire this to stream builder or query the stream
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('swimfinderlakes')
.snapshots(),
builder: (context, snapshot) {
Every time Firestore has a change, it'll trigger the StreamBuilder. U can then access the most recently update with the snapshot on the builder method. Then you can use it to update the ui accordingly.
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('swimfinderlakes')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
List pics = qn.documents
.map((it) => Photo(it['photo'].toString(),
it['name'].toString(), it['address'].toString())).toList();
return pics;
}
} else {
return CircularProgressIndicator();
}
}