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