reading data from firebase firestore collection at stream builder - flutter

I got trouble with firebase fireStore.
There is a stream builder reading data from items collection.
Inside items collection there is some fields and another collections.
I haven't any problem with fields, the problem is with collection.
how to access those collections inside stream builder?
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: CallApi().finalReference(reference: widget.finalReference),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('snapshot Error:${snapshot.error}'));
}
if (snapshot.hasData) {
var snapData = snapshot.data!.docs;
if (kDebugMode) {
print(snapData.length);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.builder(
itemCount: snapData.length,
itemBuilder: (BuildContext context, int index) {
return ListItem(
mTitle: snapData[index].get('title') ?? '',
mSubTitle: snapData[index].get('address') ?? 'empty',
mPrice: snapData[index].get('price') ?? '',
mImageUrl: snapData[index].get('gallery')[0],
mOnTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
adsTitle: snapData[index].get('title'),
adsSubTitle: snapData[index].get('subTitle'),
gallery: snapData[index].get('gallery'),
specFTitle: snapData[index].get('gallery'),
),
),
);
},
);
},
),
),
],
);
}
return const Center(child: CircularProgressIndicator());
},
),
here is firebase

Reading data from Firestore is a shallow operation. When you read a document, its subcollection are not automatically read.
So if you want to get the data from the subcollections of the current document, you will have to start a new read operation for that. If you want to show that data in the UI, you can use a new, nested StreamBuilder or FutureBuilder for that.

Related

Firestore get length data disappear in flutter stream

I make a chat using firebase firestore.
So. I tried express not read count in the Chat list.
But, Initially the number appears, but it changes to null data.
I don't know Why data chage null data?
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chatRooms')
.where('emails', arrayContainsAny: [user?.email]).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
var chatLists = snapshot.data?.docs;
if (snapshot.hasError) {
return Text('Something error...');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('is Loading...');
}
return ListView.builder(
itemCount: chatLists?.length,
itemBuilder: (context, index) {
if (chatLists?[index]['currentMsg'] != null &&
chatLists?[index]['currentMsg'] != "") {
var list = List.from(chatLists?[index]['members']);
var member = '';
if (loginUser['userName'] != null) {
for (int i = 0; i < list.length; i++) {
if (list[i] != loginUser['userName']) {
member += list[i];
}
}
}
return ListTile(
title: Row(
children: [
Text(member),
const SizedBox(
width: 20.0,
),
ChatLength(docId: chatLists![index].id, uid: user!.uid),
],
),
subtitle: SizedBox(
height: 40.0,
child: Text(
chatLists[index]['currentMsg'],
overflow: TextOverflow.ellipsis,
)),
trailing: Text(
tmFormmater(chatLists?[index]['currentTm']),
style: const TextStyle(
color: Color(0xff999999),
),
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
docId: chatLists![index].id,
title: member,
),
)),
);
} else {
return Container();
}
},
);
return Container();
},
),
class ChatLength extends StatelessWidget {
const ChatLength({super.key, required this.docId, required this.uid});
final String docId;
final String uid;
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('message_read')
.doc(docId)
.collection('message')
.where('userId', isNotEqualTo: uid)
.where('read', isEqualTo: false)
.snapshots(),
builder: (context, snapshot) {
print(snapshot.data?.size);
if (snapshot.data?.size != null) {
return Container(
child: Text('${snapshot.data?.size}'),
);
} else {
return Container();
}
},
);
}
}
===== Debug console ====
flutter: 1
flutter: null // Data changes to null immediately
message_read collection structure
message_read -> documentId -> message -> documentId -> field(userId(chat writer), read)
I'm trying get a snapshot data from firestore.
And I put the imported data into the text.
But Data changes to null immediately.
Since you use snapshots() the code is not just reading the data once, but then continues listening for changes to the data. So the two values you see mean that the snapshots() stream was triggered twice, the first time with a single message - and the second time without any messages.
My educated guess is that your code changes the read value of the document after it gets it, since it has now displayed that message to the user. But doing so means the document no longer meets the criteria of your query, so the snapshots stream gets a new event without that message in it.
Consider using a different mechanism for the query, for example I usually use a timestamp to determine what messages to show. Step by step:
Ensure each message document has a timestamp field.
For each user store (either in the database or in local storage of the app) when they last started the app.
Then request from the database the messages since they last started the app.
Make sure to start the query before you update the timestamp value, otherwise you'll never get any results.

How to get a document snapshot index

I'm new to flutter and I'm trying to pass a firestore document snapshot to another class.
I passed to the Profile class a snapshot document, and I want to indicate the index of my document, but I don't know how to get it
I have this
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: ((searchString != null) &&
(searchString.trim() != ""))
? Firestore.instance
.collection('pazienti')
.where("searchIndex",
arrayContains: searchString)
.snapshots()
: Firestore.instance
.collection('pazienti')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
return ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return Card(
elevation: 10.00,
margin: EdgeInsets.all(0.50),
child: ListTile(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[????])));
}
,
leading: CircleAvatar(
backgroundColor:
Colors.blueGrey.shade800,
),
title: Text(document['cognome'] +
" " +
document['nome']),
subtitle: Text(document['cognome'] +
" " +
document['nome']),
),
);
}).toList(),
);
}
})),
],
),
)
My problem is essentially here
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[XXXX]))
How can I get the index of the document from the map I used?
Thank you very much for your help
You just want to pass document on which tap, so you can simply pass document which you are getting from map method.
Snapshots from a query don't have a numeric index. The results from a query could change at any time between queries, and the system can not guarantee that any sort of index would be stable.
If you want to pass a document to another function, pass its unique document ID. The receiver can then query the document directly, perhaps from local cache without requiring a billed read operation at the server.
var listIndex= snapshot.data.documents.map((e) => e.data['key']);
var passIndex = listIndex.toList().indexOf(doc.data['key']);
print(passIndex);
You can simply pass the index when assigning the list
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildItem(context, data, snapshot.indexOf(data))).toList(),
);
}

How to get data from the FutureProvider in flutter

I'm trying to implement local database support in my flutter app which is being managed using Provider, now I want to make the retrieving of data obey the state management pattern, but I've been failing to.
I've tried to make a traditional Provider to achieve this but the app got stuck in a loop of requests to the database, so after some search I found the FutureProvider, but I cant find how can I get a snapshot from the data being loaded
class _ReceiptsRouteState extends State<ReceiptsRoute> {
List<Receipt> receipts = [];
#override
Widget build(BuildContext context) {
return FutureProvider(
initialData: List(),
builder: (_){
return DBProvider().receipts().then((result) {
receipts = result;
});
},
child: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).history),
),
body: Container(
child: ListView.builder(
itemBuilder: (context, position) {
final item = receipts[position];
return ListTile(
title: Text(item.date),
);
},
),
),
),
);
}
}
now my app is running as I want but not as how it should run, I used FutureBuilder to get the data from the database directly but I know it should come through the provider so I want to make it right
FutureProvider exposes the result of the Future returned by builder to its descendants.
As such, using the following FutureProvider:
FutureProvider<int>(
initialData: 0,
builder: (_) => Future.value(42),
child: ...
)
it is possible to obtain the current value through:
Provider.of<int>(context)
or:
Consumer<int>(
builder: (context, value, __) {
return Text(value.toString());
}
);
In my example I used the create parameter of FutureProvider to request the API, then then I used Consumer to get the results of the API.
FutureProvider(
create: (_) => peopleService.getAllSurvivor(),
child: Consumer<List<Survivor>>(builder: (context, survivors, _) {
return survivors == null
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: survivors.length,
itemBuilder: (context, index) {
var survivor = survivors[index];
return ListTile(
title: Text(survivor.name),
subtitle: Text(survivor.gender),
leading: Icon(Icons.perm_identity),
);
},
);
})));

Flutter and Firestore, handle data from StreamBuilder

I'm right now trying to make a Quiz like application in flutter for learning this Framework.
I've implemented Firebase Firestore to manage the application data.
From this flutter plugin documentations I read about binding a CollectionReference to a ListView and it was pretty easy.
My problem is the following.
I've got some categories to display in the Home page, I want the user to be able to select which one he wants and then store this information in a List.
With this code I can display the list:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Categorie').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Center(
child: Column(
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
height: 50.0,
width: 50.0,
),
Text('Dowload delle categorie...')
],
),
);
default:
_loadListOfCategories(snapshot);
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
var nome = document['Nome'];
print('doc: $nome');
return new CategoryWidget(
id: document.documentID,
tap: onCategoryTap,
nome: document['Nome'],
count: document['Count'],
checked: false,
);
}).toList(),
);
}
},
);
}
CategoryWidget is just a simple stateless widget which act as a ListTile.
The result is the following:
Now, how can I save a List full of Category models, which one implementing a "checked/unchecked" property, and how can I keep this List updated?
I tried using "_loadListOfCategories()" method exacly inside the builder:
void _loadListOfCategories(AsyncSnapshot<QuerySnapshot> snapshot) {
var temporalList = new List<CategoryModel>();
for (DocumentSnapshot doc in snapshot.data.documents) {
temporalList.add(new CategoryModel(
id: doc.documentID,
count: doc['Count'],
nome: doc['Nome']));
}
setState(() {
_listOfCategories = temporalList;
});
}
But I couldn't call setState() here becouse I'm actually inside the building method.
use a map, your key will be 'document.documentID' and the value a boolean.
Map map = Map() // assuming this a string document.documentID
checked: map.get(document.documentID),
in your checkbox you call
setState()=>
map.put(document.documentID, !map.get(document.documentID));

how can I show document?

I am trying to fetch document one by one ...i am using below code its work but in console its show me error like
StreamBuilderBaseState>#de08b):
The getter 'documents' was called on null.
Receiver: null
Tried calling: documents
new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Quiz").where("topice",isEqualTo: widget.topic).where("section",isEqualTo: widget.section).snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,) {
int length= snapshot.data.documents.length;
if (!snapshot.hasData)
return new Container(child: Text(""),);
return ListView(
children: <Widget>[
Text(widget.scoren.toString()),
Text(snapshot.data.documents[cunter]["Description"]),
Row(
children: <Widget>[
CupertinoButton(child: Text("COMMENTS"), onPressed: null),
RaisedButton(onPressed: (){
setState(() {
cunter++;
});
// Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>new MyApp()));
},child: Text("NEXT"),)
],
)
],
)
You are trying to access the length property of the documents list before you check if the snapshot has any data.
Try inverting those lines, like this
//Check if the snapshot has data
if (!snapshot.hasData) return Container(child: Text(""));
//If you get here it means you have data
int length= snapshot.data.documents.length;
Also, remember that in Dart 2 the new keyworkd is not needed anymore.