I have an app where admin can delete all documents in the firebase collection and add an x number of new documents, this works beautifully, but my streambuilder isn't updating properly,
the stream builder is getting back only one document everytime you delete all documents and create new ones, it only returns one, and like when you leave the app and come back, it fetches the proper amount of documents, all I can find online is that it's wrong to use a loop when querying and I've removed my for loop and am now using the map method, still, it is the same, here is my stream builder code
StreamBuilder<QuerySnapshot>(
stream: _store.collection("picks").snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<PickCard> pickCards = [];
final documentSnapshots = snapshot.data!.docs;
debugPrint(documentSnapshots.length.toString());
if (documentSnapshots.isNotEmpty) {
documentSnapshots.map((e) {
pickCards.add(
PickCard(
pickerPosition: e["pickerPosition"],
pickerName: e["pickerName"],
isPicked: e["isPicked"],
pickerEmail: e["pickerEmail"],
),);
}).toList();
dHelp.setCards(
context,
pickCards,
);
dHelp.setContributors(context, documentSnapshots.length);
}
} else {
}
the print document snapshot length is always 1 when they get created, but after refresh, the actual length updates, but in the firebase console, everything works perfectly, the documents update effectively,
here is a video of the problem https://www.dropbox.com/s/25qqnh0ttgemgf1/2022-08-16%2010-26-46.mp4?dl=0
I found that passing the stream directly to the streamBuilder was causing the stream to restart each time the build method rebuilt, which was supposed to be whenever the stream returns new data, so, it was kinda knotting over itself,
I instantiated the stream in the state then passed it to the streamBuilder, so now it's only created once in the lifetime of the page
// created this variable
late Stream<QuerySnapshot> _stream;
#override
initState() {
// gave it a value in iniState
_stream = _store.collection("picks").snapshots();
super.initState();
}
StreamBuilder<QuerySnapshot>(
stream: _stream, // then added this here
builder: (context, snapshot) {
if (snapshot.hasData) {
List<PickCard> pickCards = [];
final documentSnapshots = snapshot.data!.docs;
debugPrint(documentSnapshots.length.toString());
if (documentSnapshots.isNotEmpty) {
documentSnapshots.map((e) {
pickCards.add(
PickCard(
pickerPosition: e["pickerPosition"],
pickerName: e["pickerName"],
isPicked: e["isPicked"],
pickerEmail: e["pickerEmail"],
),);
}).toList();
dHelp.setCards(
context,
pickCards,
);
dHelp.setContributors(context, documentSnapshots.length);
}
} else {
}
Related
i have a problem with a Flutter application.
I want to receive data from a firebase firestore by an Document id.
The problem is, i cant replicate the data in one collection, so i have to use two collections.
My data ist a Measurement(String, String,..., List)
MeasurementData(String,String,Number)
As far as i understand the Firestore i cant make such a list in one collection, so i have created a second one in whitch each document consists of three arrays. I also save the id from the Measurement Document where it belongs to.
To use it in Flutter a use a StreamBuilder widget. For easer access i convert the document bevor using into a Dart Object. Because i have two Streams which should be converted to one Object i have a predessecor StreamMeasurement object which is basicly the same as the Measurement Object, but without the MeasurementData list, because this first needs to be created, instead it only saves a id for the measurementData Document.
After that i use the Rx.zip2 to create an Measurement Object from the StreamMeasurement Object and add the List to have a complete object.
Class Service
/// The firebase instance
FirebaseFirestore db = FirebaseFirestore.instance;
///Finds and returns a [Measurement] by its [id]
Stream<Measurement> getById(String id) {
Stream<StreamMeasurement> streamMeasurementStream =db.collection("measurements").doc(id)
.snapshots().map((document) => StreamMeasurement.fromJson(document.data()!));
return Rx.zip2(streamMeasurementStream, getMeasurementDataStreamById(id)
, (StreamMeasurement streamMeasurement, List<MeasurementData> measurementData) {
Measurement measurement = Measurement.fromStreamMeasurement(streamMeasurement, measurementData);
if(measurement == null)
{
//TODO Write a concrete exception to throw and catch
throw Exception('Measurement Stream is null');
}
return measurement;
}) ;
}
/// Returns a [Stream<List<MeasurementData>>] for a given Measurement id
Stream<List<MeasurementData>> getMeasurementDataStreamById(String id)
{
return db.collection("measurementData").where("measurement", isEqualTo: id).
snapshots().
map((querySnapshot) => querySnapshot.docChanges)
.expand((changes) => changes)
.map((change) => change.doc).map((document) => MeasurementData.fromJson(document.data()!));
}
Class DetailScreen
#override
Widget build(BuildContext context) {
MeasurementService measurementService = MeasurementService();
Stream<Measurement> measurementstream = measurementService.getById(
"GmfzveKeozkcfdlrk75s");
return StreamBuilder<Measurement>(
stream: measurementstream,
builder: (BuildContext context, AsyncSnapshot<Measurement> snapshot) {
if (snapshot.hasError) {
print(snapshot.error);
return Text('Something went wrong' + snapshot.error.toString());
}
if (snapshot.connectionState == ConnectionState.waiting) {
print(snapshot);
return Text("Loading" + snapshot.connectionState.toString());
}
Measurement? measurement = snapshot.data;
return SafeArea(
Print(snapshot) => I/flutter (19335): AsyncSnapshot(ConnectionState.waiting, null, null, null)
The result is, that the StreamBuilder is stuck in ConnectionState.waiting state if i use the getById(id) function
Okay, ... Question solved. The problem was a unmatched whitespace before the id
I have an application which fetches data related to videos(vedioId,description,number of views,likes) from firestore and display it in a listview using streambuilder. It also have a feature to search data. The search data is done locally in system and it works fine. But the problem happens when I try to update data when clicking like button in the list. Since the data is fetched using streambuilder as snapshots, I uses a list to store snapshots from the stream and apply modification to that list when search happens. But I cannot update the list when clicking like button after searching. I cannot update the list of video like normal list because the values have string indeces(eg:list[index]["video"]). how can I update the list having snapshot values.
Here is the code for listview and streambuilder
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: _streamController.stream,
builder: (context, snapshot) {
if (!snapshot.hasData){
return Center(child: CircularProgressIndicator());}
else{
allresults = snapshot.data!.docs;
if(searchb00l) {
allresultslist = snapshot.data!.docs;
}else{
allResults = resultsList;
}ListView.builder(
controller: _mainScroll,
itemCount: allResults.length,
itemBuilder: (context, index) {List _likList = allResults[index]["lik"];......}
Here is the code for searching
onSearchChanged(String val){
var showResults = [];
if(val != "") {
searchb00l = true;
for(var titlePattern in allresults){
var title = titlePattern['des'].toLowerCase();
if(title.contains(val.toLowerCase())) {
showResults.add(titlePattern);
print(showResults.length);
}
}
} else {
searchb00l = false;
showResults = List.from(allresults);
}
resultsList = showResults;
}
as you can see the data is displayed from the list allresults, I want to update this list after clicking like button of the list of video. But I cannot update this list using this code
allResults[index]["lik"]=newlikearray;
is there any way to update this array containing snapshot.data.docs value
I'm writing an app that communicates with a server. The app will have a listview with items inside that need to be updated periodically (every x seconds) and I'm trying to figure out the best way to accomplish this.
Let's say I have a Stream that sends a request to a server every 5 seconds. I yield the result, but how can I receive this data inside of a view and update it?
for example:
Stream:
Stream<double> progress(int id) async* {
while (true) {
await Future.delayed(const Duration(seconds: 5));
double progress = await api.getProgressFor(id: id);
yield progress;
}
}
How could I create a widget, say a LinearProgressIndicator that will listen for yields from this stream and update when they are sent.
The best way is to use a StreamBuilder. Here is a sample showing where you call your stream and where you display your ListView or similar.
#override Widget build(BuildContext context) {
return StreamBuilder <int>(
stream: callProgressStream ,
builder: (context, snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
else {
// Your code here
return ListView();}
});
}
Let me know if this does not help.
I'm try to get sub collection from each doc ex.clothes,notifer witch I have more docs , that means I don't know its id ,My bossiness Logic was to fetch the main collection for getting all the documents and then for each doc get its sub collection and I did that with Future implementation ,but I can't do it using Stream to return the final Sub Collection SnapShots ex.properitres for listing to changes . by Future it rebuild every time and if I stop widget rebuilding by AutomaticKeepAliveClientMixin I could not get any Firesotre changes . Thanks in advance .
here is my Future implementation but again I need this implementation by Stream ^_^:
Future<List<Category>> getPropertiesDocs() async {
List<QueryDocumentSnapshot> _firstListOfDocs = [];
List<Category> _categorListOfDocs = [];
List<QueryDocumentSnapshot> _secoudListOfDocs = [];
final QuerySnapshot result = await _firebaseFirestore.collection('categories').get();
result.docs.forEach((element) {
// print(element.id);
_firstListOfDocs.add(element);
});
for (var i in _firstListOfDocs) {
final QuerySnapshot snapshot2 = await i.reference.collection("properties").get();
snapshot2.docs.forEach((element) {
_secoudListOfDocs.add(element);
_categorListOfDocs.add(Category.fromSnapShpt(element));
});
}
_firstListOfDocs.clear();
_secoudListOfDocs.clear();
return _categorListOfDocs;
}
From your future implementation,
You want to get all documents in categories collection.
For each document in categories collection, you want to get the properties subcollection.
For the first requirement, we can simply stream the categories collection.
For the second requirement, it is not advisable to stream properties collection from each categories subcollection. This won't scale well with large datasets.
Instead we will stream the collectionGroup properties. Streaming the collection group properties will fetch all collections with the name properties (no matter the location). To effectively use this, no other collection should be named properties (except the ones you want to fetch), or you rename your collection to something distinct like properties_categories.
// this streamBuilder will fetch stream for categories collection.
StreamBuilder<QuerySnapshot>(
stream: _firebaseFirestore.collection('categories').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot<Delivery>> snapshot) {
if (snapshot.hasError) return Message();
if (snapshot.connectionState == ConnectionState.waiting)
return Loading();
print('categories snapshot result');
print(snapshot.data.docs.map((e) => e.data()).toList());
// _firstListOfDocs is given below (renamed to _categoryDocs)
List<QueryDocumentSnapshot> _categoryDocs = snapshot.data.docs;
// this streamBuilder will fetch all documents in all collections called properties.
return StreamBuilder<QuerySnapshot>(
stream: _firebaseFirestore.collectionGroup('properties').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> propertiesSnapshot) {
if (propertiesSnapshot.hasError) return Message();
if (propertiesSnapshot.connectionState == ConnectionState.waiting)
return Loading();
print('properties snapshot result');
print(propertiesSnapshot.data.docs.map((e) => e.data()).toList());
// _secoudListOfDocs is given below (and renamed to _propertiesDocs)
List<QueryDocumentSnapshot> _propertiesDocs = propertiesSnapshot.data.docs;
// _categorListOfDocs is given below (and renamed to _categories)
List<Category> _categories = propertiesSnapshot.data.docs
.map((e) => Category.fromSnapShpt(e)).toList();
// return your widgets here.
return Text('Done');
},
);
},
)
If your reason for fetching categories collection data is simply to loop over it and fetch properties collection, then you can delete the first streamBuilder above since we do not need to use it to fetch properties collection.
I want to return my all documents in firestore my document in Identifier based on name but when I do my function its return to me With the same number I have documents but the name its different instance of DocumentSnapshot but I need to return same names I have. How can I do this? Below is the code I am using
Widget build(BuildContext context) {
// TODO: implement build
return StreamBuilder < QuerySnapshot > (
stream: Firestore.instance.collection("Institute")
.document(widget.id).collection("Ravs").snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print('list of docment:${snapshot.data.documents.toList()}');
};
return CircularProgressIndicator();
}
);
}
The Instance of DocumentSnapshot log is saying that you are interacting with the Snapshot object itself. which is what your code is indeed doing.
To get access to the data inside the Snapshot you have to add the .data() call next to your Snapshot, so your stream should look like this:
stream: Firestore.instance.collection("Institute").document(widget.id)
.collection("Ravs").snapshots().data()