2

In the example provided here for infinite scrolling, it adds 10 more items when it reaches the end.

if (index >= _suggestions.length) {
      // ...then generate 10 more and add them to the suggestions list.
      _suggestions.addAll(generateWordPairs().take(10));
}

What I don't understand is how does the ListView get updated even when they are not calling setState()?

Flake
  • 1,386
  • 17
  • 31

1 Answers1

2

Well noted. We will need a little more of the code to understand this.

  Widget _buildSuggestions() {
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      itemBuilder: (context, i) {
        if (i.isOdd) return new Divider();

        final index = i ~/ 2;
        if (index >= _suggestions.length) {
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      },
    );
  }

Looking at the docs for ListView and SliverChildBuilderDelegate I see the statement:

builder will be called only for indices greater than or equal to zero and less than childCount (if childCount is non-null).

So what's happening is we're not providing an itemCount to the ListView, and this makes it believe it has infinite elements. So we don't need to setState to tell it that we have 10 more items; it never knew how many items we had to begin with. As long as the items are there when the ListView get to them it will be happy.

Strictly speaking this list does not "track" changes to _suggestions. By giving a null itemCount you're telling ListView that you have infinite items; besides, the existing items in _suggestions never change, they're just appended. In this example flutter only needs to be told of changes to _saved.

On the other hand if we had something like ListView.builder(itemCount: _suggestions.length ...), then we would need to make sure we call setState when the list length changes.

Edman
  • 5,335
  • 29
  • 32
  • Edman 's answer is correct. But don't follow this tutorial if you try to achieve "Load more data on scroll". Their example is a pretty ugly way of doing it. – Rémi Rousselet May 19 '18 at 20:29
  • @RémiRousselet out of curiosity, would you care to explain what's bad? Is it that they're refreshing the list once it hits the end? Or how would you recommend this be done instead? – Edman May 19 '18 at 20:41
  • First, because it works only with synchronous data load (so no http). Second, because that's against the principle of "One function, one functionality". Using `itemBuilder` to lazy load is a hack. The Flutter way of achieving this kind of things is using a `ScrollController` which is listenable. example here https://stackoverflow.com/questions/49508322/flutter-listview-lazy-loading/49509349#49509349 – Rémi Rousselet May 19 '18 at 20:53
  • So how does it track the changes on the List? Does the ListView keep polling the List for its contents? – Flake May 20 '18 at 05:17
  • I got it that `itemCount` is null, so it expects infinite items. But there has to be some trigger which notifies it that items have been added.What is that trigger? – Flake May 20 '18 at 09:27
  • 1
    There's really no such trigger. As the user scrolls `ListView` tells its builder to give it the item at index `i`, and if we're asked for an index we don't have, we create it on the fly. At no moment is the list view notified of new elements. `ListView` is not even aware of `_suggestions` at all. It knows it has an `itemBuilder` that will create widgets when it needs. – Edman May 20 '18 at 09:51
  • 1
    if I understand it correct, it works only because the new items are generated synchronously. And if the fetching were to be async, then this should not work. correct? – Flake May 20 '18 at 12:59
  • 1
    Correct. If fetching were async it could happen that the listview asks for an entry you don't yet have, in which case you will want to notify the listview once you have it. – Edman May 20 '18 at 13:11