Flutter ListView lazy loading
Thanks for Rémi Rousselet's approach, but it does not solve all the problem. Especially when the ListView has scrolled to the bottom, it still calls the scrollListener a couple of times. The improved approach is to combine Notification Listener with Remi's approach. Here is my solution:
bool _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollEndNotification) {
if (_controller.position.extentAfter == 0) {
loadMore();
}
}
return false;
}
@override
Widget build(BuildContext context) {
final Widget gridWithScrollNotification = NotificationListener<
ScrollNotification>(
onNotification: _handleScrollNotification,
child: GridView.count(
controller: _controller,
padding: EdgeInsets.all(4.0),
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this would produce 2 rows.
crossAxisCount: 2,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0,
// Generate 100 Widgets that display their index in the List
children: _documents.map((doc) {
return GridPhotoItem(
doc: doc,
);
}).toList()));
return new Scaffold(
key: _scaffoldKey,
body: RefreshIndicator(
onRefresh: _handleRefresh, child: gridWithScrollNotification));
}
You can listen to a ScrollController
.
ScrollController
has some useful information, such as the scrolloffset and a list of ScrollPosition
.
In your case the interesting part is in controller.position
which is the currently visible ScrollPosition
. Which represents a segment of the scrollable.
ScrollPosition
contains informations about it's position inside the scrollable. Such as extentBefore
and extentAfter
. Or it's size, with extentInside
.
Considering this, you could trigger a server call based on extentAfter
which represents the remaining scroll space available.
Here's an basic example using what I said.
class MyHome extends StatefulWidget {
@override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
ScrollController controller;
List<String> items = List.generate(100, (index) => 'Hello $index');
@override
void initState() {
super.initState();
controller = ScrollController()..addListener(_scrollListener);
}
@override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Scrollbar(
child: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return Text(items[index]);
},
itemCount: items.length,
),
),
);
}
void _scrollListener() {
print(controller.position.extentAfter);
if (controller.position.extentAfter < 500) {
setState(() {
items.addAll(List.generate(42, (index) => 'Inserted $index'));
});
}
}
}
You can clearly see that when reaching the end of the scroll, it scrollbar expends due to having loaded more items.