1

I want to use Flutter’s ListView to display a list of items, and the data of the items are obtained from a RESTful API with pagination support. E.g.

GET /transactions?page=1&limit=100
{
  "transactions": [ ... ]
  "pagination": {
     "total": 1000
  }
}

page: The page to fetch, such as 1, 2, 3, ...
limit: The size of a page.
total: Total number of items on the server. It can be large.

I know I can use ListView.builder(), and let the itemBuilder callback argument returns a widget wrapped in FutureBuilder, and call the API asynchronously to fetch page for the index argument (with some logic caching the pages so that each page is only fetched once).

However, ListView.builder() also has a itemCount argument. I don’t know the total count of items before fetching the first page. Without the itemCount argument, the itemBuilder may be called with index exceeding the total number of items.

One way that comes to my mind is to wrap the ListView in another FutureBuilder and asynchronously call the API to get the total count. However, the total count may change over time. For example, the total count can be 1000 initially. But when the user scrolls to the end, some items are removed from the server by another user, and the total count becomes 950. When it happens, it’s still possible that itemBuilder is called with index out of bound.

What should I do to deal with this situation?

someone
  • 100
  • 2
  • 8

2 Answers2

2

If I understand you well, and from what I can see in the response. you want to do a pagination thing. use infinite_scroll_pagination

1

This shouldn't be an issue. You should call ListView.builder() only when you already have the API items properly returned. And if that's the case, you can tell the length of the items (after they have successfully returned), and use this length as the itemCount of ListView.builder().

Any other solution you use (asides ListView.builder()) will still require the API to return the data successfully before rendering the UI (based on the returned data).

If you understand the above, then your problem should be solved.


But if it still doesn't solve it, use ListView with children instead of the .builder method.

After the API has returned the items, call .map method on the returned List/Iterable (from the API). This method should return the widgets that your .builder() would have returned. You are calling this .map method against children property of ListView. Something like:

ListView(
  // where items is from the API
  children: items.map((i) => Text(i)).toList(), 
)

You can choose to use spread operator instead of appending .toList(). Something like:

ListView(
  // where items is from API
  children: [...items.map((i) => Text(i))],
)

Another solution you have is to display a "show more" button at the end of the list. Then fetch (with pagination) and append to items when the user clicks the button.

Obum
  • 1,485
  • 3
  • 7
  • 20
  • But if there're many items, say one million, and the page size is limited to 100, we need 10,000 API calls before showing the list view. – someone Oct 12 '22 at 16:59
  • It depends on you. You can keep showing a CircularProgressIndicator or a percentage based LinearProgressIndicator while doing the 10000 API calls. And maybe you could disburse the fetches to another Dart Isolate. But sincerely, you shouldn't have to make the user wait for 10000 API calls. If that's the only way out, then the backend needs to ensure pagination. The above answer was with respect to the original post. You already made it clear that you were paginating by 100 and was uncertain of the remains. I'm surprised you are talking about one million. Or what exactly is the problem? – Obum Oct 13 '22 at 09:25
  • Another solution you have is to display a "show more" button at the end of the list. Then fetch (with pagination) and append to items when the user clicks the button. – Obum Oct 13 '22 at 12:15
  • Sorry, let me explain it a little bit more. Let's say the API can return at most 100 items at once (one page). And we can use a parameter to specify the specific page we want. The total number of items could be large. Using a "show more" is a good alternative. And I may also automatically append the next page when the user scrolls to the end. It requires more code, though. I originally expected that using the built-in `ListView` would be enough. Thanks. – someone Oct 13 '22 at 16:31
  • You are welcome, please accept as answer. I added the "show more" button in the answer. – Obum Oct 13 '22 at 16:54
  • 1
    Sorry that I can choose only one accepted answer. The answer from @ahmed-hussein is simpler. But still, thank you for your suggestions. – someone Oct 14 '22 at 17:06
  • Oh that's fine. – Obum Oct 15 '22 at 19:19