0

I've looked at a number (~30) stackoverflow posts that are all similar, and yet none seem to be able to make me understand what I'm doing wrong. Some examples:

setState in futureBuilder Flutter - Wait setState in FutureBuilder then load component

Some answers, like the one immediately above, say that setState should never be used with FutureBuilder, Aand yet other upvoted and accepted answers say that doing so is the solution to my problem. Example:

Reload data when using FutureBuilder

So what I am actually doing is I want to implement pagination for some product documents. So I pull data with this method:

 static Future<List<String>> limitNumberByField(String collection, String field,
  String desiredValue, int limit, String? startAfterDocId) async {

List<String> docs = [];
FirebaseFirestore db = FirebaseFirestore.instance;
CollectionReference collectionRef = db.collection(collection);

Query filteredQuery = collectionRef
  .where(field, isEqualTo: desiredValue)
  .limit(limit);

if (startAfterDocId != null) {
  DocumentSnapshot startAfterDoc = await collectionRef.doc(startAfterDocId).get();
  filteredQuery = filteredQuery.startAfterDocument(startAfterDoc);
}

await filteredQuery.get().then((QuerySnapshot querySnapshot) => {
  querySnapshot.docs.forEach((doc) => {
    docs.add(doc.id)
  })
});

return docs;

}

This works as expected. The first page also displays exactly as I want. But when I press a button to load the next page, in which I call setState(), it just breaks. The FutureBuilder's future calls a function that calls the above function to get a list of DocumentID's and then builds buttons according to each. But instead of creating a new list on button press, it displays my custom message that displays only when there is no data in snapshot and when the connectionState is done.

The code:

https://pastebin.com/TfGbD82Y

The build method in the above link:

 @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _getProducts(context),
      builder: (context, snapshot) {
        if (!snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
          return const Text(
            'ERROR: Could not load data',
          );
        } else if (snapshot.hasData) {
          return Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  _curPage > 1 ? LeftArrow(callback: () => setState(() {
                    _curPage--;
                  })) : Container(),
                  _nextPageAvailable ? RightArrow(callback: () => setState(() {
                    _curPage++;
                  })) : Container(),
                ],
              ),
              Padding(
                padding: const EdgeInsets.all(10.0),
                child: Container(
                  padding: const EdgeInsets.all(8.0),
                  width: double.infinity,
                  height: 400,
                  decoration: _boxDecoration(),
                  child: SingleChildScrollView(
                    scrollDirection: Axis.vertical,
                    child: Column(
                      children: [
                        SectionLabel(AppLocalizations.of(context)!.availableProducts),
                        Column(
                          children: snapshot.data!,
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ],
          );
        } else {
          return const CircularProgressIndicator();
        }
      }
    );
  }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 2
    instead of that `setState` and `FutureBuilder` use `StreamBuilder` - this is how you should go with multiple async values – pskink Apr 17 '23 at 10:53
  • @pskink this is a marked improvement and excellent suggestion, but the above problem persists – Riverreed Ward Apr 17 '23 at 11:26
  • with `StreamBuilder`? all you need is to emit a new value in your stream and `StreamaBuilder` will rebuild with that updated snapshot (for example you can use `StreamController` for that) – pskink Apr 17 '23 at 11:41
  • @pskink I don't know what to tell you man. I still have the same problem. Everything now loads in a stream, but when I press on the next page button, a few seconds delay, then it displays the String error message again. – Riverreed Ward Apr 17 '23 at 11:47
  • what do you see on the logs if you add `print(snapshot)`? – pskink Apr 17 '23 at 11:54
  • @pskink AsyncSnapshot>(ConnectionState.active, [CategorySelectionButton, CategorySelectionButton, CategorySelectionButton, CategorySelectionButton, CategorySelectionButton, CategorySelectionButton, CategorySelectionButton], null, null) – Riverreed Ward Apr 17 '23 at 12:12
  • and where is the second snapshot? what you sent is the initial one, where is the next? – pskink Apr 17 '23 at 13:26
  • @pskink I/flutter ( 9774): AsyncSnapshot>(ConnectionState.active, [CategorySelectionButton, CategorySelectionButton, CategorySelectionButton], null, null) They looked identical, so I didn't bother post both. Should have specified – Riverreed Ward Apr 17 '23 at 13:36

0 Answers0