1

I have a FutureBuilder on the homepage of my app that returns a SliverFixedExtentList after the future is complete. It looks something like this:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:project_spruce/DataBase/DataBaseConnection.dart';
import 'package:project_spruce/states/UserState.dart';
import 'package:project_spruce/widgets/FabBottomAppBar.dart';
import 'package:project_spruce/widgets/FoodItemCard.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    final List<DocumentSnapshot> _documents = List<DocumentSnapshot>();
    String uid = Provider.of<UserState>(context).firebaseUser.uid;
    return FutureBuilder(
      future: Provider.of<DataBaseConnection>(context).getQuerySnapshot('eatenFoods/'+ uid +'/food'),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
        Widget child;
        if (snapshot.hasData){
          _documents.clear();
          snapshot.data.documents.forEach((doc)=> _documents.add(doc));
          child = SliverFixedExtentList(
            itemExtent: 150,
            delegate: new SliverChildBuilderDelegate(
              (context, index) => FoodItemCard(_documents[index]),
              childCount: _documents.length,
            ),
          );
        }else {
          child = SliverToBoxAdapter(child: CircularProgressIndicator());
        }
        return Scaffold(
            floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
            floatingActionButton: FloatingActionButton(
              onPressed: () => Navigator.pushNamed(context, '/camera_screen'),
              tooltip: 'Add',
              elevation: 2,
              child: Icon(Icons.add),
              backgroundColor: Theme.of(context).primaryColor,
              foregroundColor: Colors.white,
            ),
            bottomNavigationBar: FabBottomAppBar(
              backgroundColor: Theme.of(context).primaryColor,
              color: Colors.white,
              selectedColor: Colors.white,
              iconSize: 30,
              notchedShape: CircularNotchedRectangle(),
              items: [
                FabBottomAppBarItem(iconData: Icons.search, text: 'Search'),
                FabBottomAppBarItem(iconData: Icons.pie_chart, text: 'Statistics'),
                FabBottomAppBarItem(
                    iconData: Icons.storage, text: 'Food Inventory'),
                FabBottomAppBarItem(iconData: Icons.person, text: 'Profile'),
              ],
              onTabSelected: (int tabIndex) {
                switch (tabIndex) {
                  case 0:
                    {
                      // Search
                      Navigator.pushNamed(context, '/search');
                    }
                    break;
                  case 1:
                    {
                      // Statistics
                      Navigator.pushNamed(context, '/stats');
                    }
                    break;
                  case 2:
                    {
                      // Inventory
                      Navigator.pushNamed(context, '/inventory');
                    }
                    break;
                  case 3:
                    {
                      // Profile
                      Navigator.pushNamed(context, '/profile');
                    }
                    break;
                }
              },
            ),
            body: new CustomScrollView(
              slivers: <Widget>[
                SliverAppBar(
                  //title: const Text('Home Page'),
                  actions: <Widget>[
                    IconButton(
                      icon: const Icon(Icons.notifications),
                      tooltip: 'Notifications',
                      onPressed: () => (){}, //(Navigator.pushNamed(context, '/notifications')),
                    ),
                  ],
                  floating: true,
                  pinned: true,
                  snap: false,
                  forceElevated: true,
                  expandedHeight: 200.0,
                  flexibleSpace: FlexibleSpaceBar(
                    title:
                        Text("Hi, " + Provider.of<UserState>(context).user.name),
                  centerTitle: false,
                  ),
                ),
                child,
              ],
            )
            );
      }
    );
  }
}

The future gets a list of documents from the database, then passes them, in order, to a helper class that wraps the data up in a Card. The problem is that this implementation puts the most recent documents on the bottom, and I want them on the top. I can change the order I retrieve the documents from the database Firestore.instance.collection(path).orderBy('date', descending: true).getDocuments();, but since the list doesn't get completely rebuilt each time I return to the page, the new data doesn't show up at all unless I rebuild the app. I also tried setting reverse: true in the custom scroll view, but this reverses everything, including the SliverAppBar.

Is there some way that I can just flip the way the SliverFixedExtentList is built, so that the higher indexes are on the top?

kyle bates
  • 40
  • 6

1 Answers1

2

Instead of trying to reverse the SliverFixedExtentList, you can reverse the items...All you have to do is reverse the _documents after the for each loop

Note where I added _documents = _documents.reversed.toList() and the setState

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:project_spruce/DataBase/DataBaseConnection.dart';
import 'package:project_spruce/states/UserState.dart';
import 'package:project_spruce/widgets/FabBottomAppBar.dart';
import 'package:project_spruce/widgets/FoodItemCard.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    final List<DocumentSnapshot> _documents = List<DocumentSnapshot>();
    String uid = Provider.of<UserState>(context).firebaseUser.uid;
    return FutureBuilder(
        future: Provider.of<DataBaseConnection>(context).getQuerySnapshot('eatenFoods/'+ uid +'/food'),
        builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
          Widget child;
          if (snapshot.hasData){
            setState(() {
              _documents.clear();
              snapshot.data.documents.forEach((doc)=> _documents.add(doc));
              child = SliverFixedExtentList(
                itemExtent: 150,
                delegate: new SliverChildBuilderDelegate(
                      (context, index) => FoodItemCard(_documents[index]),
                  childCount: _documents.length,
                ),
              );
            });
          }else {
            child = SliverToBoxAdapter(child: CircularProgressIndicator());
          }
          return Scaffold(
              floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
              floatingActionButton: FloatingActionButton(
                onPressed: () => Navigator.pushNamed(context, '/camera_screen'),
                tooltip: 'Add',
                elevation: 2,
                child: Icon(Icons.add),
                backgroundColor: Theme.of(context).primaryColor,
                foregroundColor: Colors.white,
              ),
              bottomNavigationBar: FabBottomAppBar(
                backgroundColor: Theme.of(context).primaryColor,
                color: Colors.white,
                selectedColor: Colors.white,
                iconSize: 30,
                notchedShape: CircularNotchedRectangle(),
                items: [
                  FabBottomAppBarItem(iconData: Icons.search, text: 'Search'),
                  FabBottomAppBarItem(iconData: Icons.pie_chart, text: 'Statistics'),
                  FabBottomAppBarItem(
                      iconData: Icons.storage, text: 'Food Inventory'),
                  FabBottomAppBarItem(iconData: Icons.person, text: 'Profile'),
                ],
                onTabSelected: (int tabIndex) {
                  switch (tabIndex) {
                    case 0:
                      {
                        // Search
                        Navigator.pushNamed(context, '/search');
                      }
                      break;
                    case 1:
                      {
                        // Statistics
                        Navigator.pushNamed(context, '/stats');
                      }
                      break;
                    case 2:
                      {
                        // Inventory
                        Navigator.pushNamed(context, '/inventory');
                      }
                      break;
                    case 3:
                      {
                        // Profile
                        Navigator.pushNamed(context, '/profile');
                      }
                      break;
                  }
                },
              ),
              body: new CustomScrollView(
                slivers: <Widget>[
                  SliverAppBar(
                    //title: const Text('Home Page'),
                    actions: <Widget>[
                      IconButton(
                        icon: const Icon(Icons.notifications),
                        tooltip: 'Notifications',
                        onPressed: () => (){}, //(Navigator.pushNamed(context, '/notifications')),
                      ),
                    ],
                    floating: true,
                    pinned: true,
                    snap: false,
                    forceElevated: true,
                    expandedHeight: 200.0,
                    flexibleSpace: FlexibleSpaceBar(
                      title:
                      Text("Hi, " + Provider.of<UserState>(context).user.name),
                      centerTitle: false,
                    ),
                  ),
                  child,
                ],
              )
          );
        }
    );
  }
}

Hope this works

Josteve
  • 11,459
  • 1
  • 23
  • 35
  • With that, I get the same problem as when I tried to grab the documents in descending order. It builds the list right the first time, but when a new document gets added, it becomes the new 0 index, and since the 0 index is already built, it doesn't show up. – kyle bates Mar 17 '20 at 04:09
  • Apart from the for each loop, is there any other place where you add to the _documents list? – Josteve Mar 17 '20 at 09:59
  • No, just in the for each loop is where I add to _documents. I first grab a snapshot from Firestore, then I clear the _documents list and use the for each loop to load each individual document into the list. – kyle bates Mar 17 '20 at 17:23
  • and reversing the list doesn't work for you? Can you drop your whole code? – Josteve Mar 17 '20 at 17:28
  • Yes, I've edited the post to reflect the full code. Like I said before though, the list is coming in in the right order, and when it gets a fresh build, it looks great. But since it isn't rebuilding the entire SliverFixedExtentList every time something changes, it doesn't call the build on indexes that have already been built, so they don't get updated. – kyle bates Mar 17 '20 at 18:13
  • Whoops, I accidentally edited your answer instead of my question. Now I've updated my post – kyle bates Mar 17 '20 at 18:17
  • Ok I think I understand you now....You are meant to work with a StatefulWidget and keep the re-assigning in setState – Josteve Mar 17 '20 at 19:35
  • Check it out now – Josteve Mar 17 '20 at 19:38
  • Sorry for the late reply @Josteve, I've had to put the project on the back burner due to recent events. I tried making it a stateful widget, however, I get an error "This widget cannot be marked as needing to build, because the framework is already in the process of building widgets" for trying to call setState in a FutureBuilder. – kyle bates Apr 03 '20 at 19:31