You want to create a ListView in flutter with lazy loading in up and down direction with boundaries?
Example: You want to display a scoreboard with 100.000 entries and want to start displaying items from 50.000. New entries should load automatically if scrolling up or down until entry 0 or entry 100.000 is reached (boundaries).
If yes, awesome, then that’s the right article for you.
There are multiple tutorials available explaining how to implement lazy loading with flutter. Unfortunately, I couldn’t find a guide so far which explains how to create a ListView which supports lazy loading in the up and down direction with specified boundaries. Therefore, I decided to create this article.
Let’s explain it with an example. The goal is to create the following list view:

1. Add the library bidirectional_listview to your project
Luckily, there’s already a library called bidirectional_listview which we’ll use for lazy loading in the up and down direction with boundaries.
Therefore, integrate bidirectional_listview into your project by adding it to pubspec.yaml:
dependencies: bidirectional_listview: ^1.0.1+1
2. Create an initial BidirectionalListView Widget
Let’s create an initial widget which contains a BidirectionalListView, first, without lazy loading.
Therefore, let’s create a simple class MyHome.dart. Please note: Executing this code will throw an error, because scroll boundaries have to be set. This will be added in step 3.
class MyHome extends StatefulWidget { @override _MyHomeState createState() => new _MyHomeState(); } class _MyHomeState extends State<MyHome> { static const double kItemHeight = 30; BidirectionalScrollController controller; Map<int, String> items = new Map(); double oldScrollPosition = 0.0; @override void initState() { super.initState(); for (int i = -10; i <= 10; i++) { items[i] = "Item " + i.toString(); } controller = new BidirectionalScrollController() ..addListener(_scrollListener); } @override void dispose() { controller.removeListener(_scrollListener); super.dispose(); } @override Widget build(BuildContext context) { // find out item counts List<int> keys = items.keys.toList(); keys.sort(); int negativeItemCount = keys.first; int itemCount = keys.last; return new Scaffold( body: new Scrollbar( child: new BidirectionalListView.builder( controller: controller, physics: AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { return Container( child: Text(items[index]), height: kItemHeight, padding: EdgeInsets.all(0), margin: EdgeInsets.all(0)); }, itemCount: itemCount, negativeItemCount: negativeItemCount.abs(), ), ), ); } void _rebuild() => setState(() {}); // Reload new items in up and down direction and update scroll boundaries void _scrollListener() { // Will be filled in next step } }
3. Add lazy loading in up and down direction and scroll boundaries
Eventually, you already noticed that the method _scrollListener wasn’t filled in step 2.
In this step, we’re filling the _scrollListener method in order to load new list view items and to set scroll boundaries.
// Reload new items in up and down direction and update scroll boundaries void _scrollListener() { // detect if scrolling up or down bool scrollingDown = oldScrollPosition < controller.position.pixels; // find out item counts List<int> keys = items.keys.toList(); keys.sort(); int negativeItemCount = keys.first.abs(); int itemCount = keys.last; // calculate scroll border from where to load new items double positiveReloadBorder = (itemCount * kItemHeight - 3 * kItemHeight); double negativeReloadBorder = (-(negativeItemCount * kItemHeight - 3 * kItemHeight)); // reload items bool rebuildNecessary = false; if (scrollingDown && controller.position.pixels > positiveReloadBorder) { for (int i = itemCount + 1; i <= itemCount + 20; i++) { items[i] = "Item " + i.toString(); } rebuildNecessary = true; } else if (!scrollingDown && controller.position.pixels < negativeReloadBorder) { for (int i = -negativeItemCount - 20; i < -negativeItemCount; i++) { items[i] = "Item " + i.toString(); } rebuildNecessary = true; } // set new scroll boundaries try { BidirectionalScrollPosition pos = controller.position; pos.setMinMaxExtent( -negativeItemCount * kItemHeight, itemCount * kItemHeight); } catch (error) { print(error.toString()); } if (rebuildNecessary) { _rebuild(); } oldScrollPosition = controller.position.pixels; }
Finally, that’s it :-).
Sources:
- Post Image: Pixabay-Link, Author: geralt, Pixabay-License
thanks, but…. what is itemCount && negativeItemCount ???
Sorry, my mistake. All ok
Hello,
Thank you for this tutorial. I find an issue when I try to execute this code, the list appear for element 0 to 10, I can see there’re in a scrollview because there’s a scroll limit animation when I go up and down but the other elements are not generated. I’m blocked with the initial list.
I’ve try to execute the code founded on the Git of the project but I’ve got the same results.
I execute the last version on a Samsung Galaxy A51.
Dear Sébastien,
I’m sorry that the code doesn’t work for you.
To reproduce this issue, I started the example code in iOS simulator and Android simulator. Unfortunately, I couldn’t reproduce this issue.
Could you please try the example above without any modification again on your side?
Thanks,
Christoph