Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter ListView Jumps To Top

I am creating a news feed for my app and as I scroll down data is fetched from Firestore. As soon as I scroll up the listview literally snaps to the very top skipping all the other items in between. The listview works fine if I load it with static data but when i pull data from Firestore, the issue reemerges. I am not sure what's causing this behaviour.

Video demonstrating the irregular scrolling behaviour

Here is my code

return StreamBuilder(
            stream: Firestore.instance.collection(Constants.NEWS_FEED_NODE)
                .document("m9yyOjsPxV06BrxUtHdp").
            collection(Constants.POSTS_NODE).orderBy("timestamp",descending: true).snapshots(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (!snapshot.hasData)
                return Center(child: CircularProgressIndicator(),);

              if (snapshot.data.documents.length == 0)
                return Container();
              //after getting the post ids, we then make a call to FireStore again
              //to retrieve the details of the individual posts
              return ListView.builder(
                  itemCount: snapshot.data.documents.length,
                  itemBuilder: (_, int index) {
                    return FeedItem2(feed: Feed.fireStore(
                        snapshot: snapshot.data.documents[index]),);
                  });
            },
          );
like image 628
spongyboss Avatar asked Jul 26 '18 10:07

spongyboss


2 Answers

Reason for the problem:

ListView, and ScrollViews in general, tend to dispose of the children that are not currently visible on the screen. When we try to scroll back to the child, the child is reinitialized from scratch. But in this case, our child is a FutureBuilder; re-initializing it creates a progress indicator again just for a part of a second, then creates the page once again. This confuses the scrolling mechanism, throwing us around in non-deterministic ways.

Solution:

One way to solve this is to make sure that the progress indicator has the exact same size of the page, but in most cases, that is not too practical. So, we will resort to a method that is less efficient, but that will solve our problems; we will prevent ListView from disposing of the children. In order to do that, we need to wrap each child — that is, each FutureBuilder, with an AutomaticKeepAliveClientMixin. This mixin makes the children ask their parent to keep them alive even when off-screen, which will solve our problem. So:

  1. Replace the FutureBuilder in your code with KeepAliveFutureBuilder.
  2. Create the KeepAliveFutureBuilder widget:
class KeepAliveFutureBuilder extends StatefulWidget {

  final Future future;
  final AsyncWidgetBuilder builder;

  KeepAliveFutureBuilder({
    this.future,
    this.builder
  });

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

class _KeepAliveFutureBuilderState extends State<KeepAliveFutureBuilder> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: widget.future,
      builder: widget.builder,
    );
  }

  @override
  bool get wantKeepAlive => true;
}
  • This widget is just a wrapper around the FutureBuilder. It is a StatefulWidget whose State extends the State class with the AutomaticKeepAliveClientMixin.
  • It implements the wantKeppAlive getter, and makes it simply return true, to denote to the ListView that we want this child to be kept alive.
like image 89
Dmitriy Blokhin Avatar answered Sep 20 '22 06:09

Dmitriy Blokhin


Just had the same issue using Flutter 1.17.3. Based on the comment from @Ashton-Thomas here: Flutter ListView Jumps To Top I added:

cacheExtent: estimatedCellHeight * numberOfItems,

to my ListView constructor and the problem went away. It has drastically increased the responsiveness of the entire list as well.

Note: my list only has about 50 items in it, so the cacheExtent isn't extremely high. May need to adjust if you have hundreds/thousands of items.

like image 26
Dave Wood Avatar answered Sep 21 '22 06:09

Dave Wood