I'm trying to implement some endless scroll pagination in a SingleChildScrollView
.
I'm setting 2 const values:
///describes how many items are loaded into right away
const int initialWidgetsLoadCount = 3;
///describes how many items are loaded after the initial load
///(when triggered by reaching the end of the SingleChildScrollView)
const int nextWidgetsLoadCount = 1;
For this minimal example I'm just filling up a Column
with Text
widgets:
SingleChildScrollView(
controller: _scrollController,
child: Column(children: _texts),
)
For adding Text
widgets I use this method:
void _addTextWidgets(int numberOfWidgets) {
setState(() {
for (int i = 0; i < numberOfWidgets; i ) {
_texts.add(Text("someText${ counter}"));
}
});
}
Now for making the pagination work, in initState()
I first add some initial widgets and start listening to the scrollbar reaching the bottom to load more:
@override
void initState() {
//this is the initial fill request
_addTextWidgets(initialWidgetsLoadCount);
_scrollController.addListener(() {
if (_scrollController.position.maxScrollExtent ==
_scrollController.offset) {
//the bottom of the scrollbar is reached
//adding more widgets
_addTextWidgets(nextWidgetsLoadCount);
}
});
super.initState();
}
The problem:
If I set the initialWidgetsLoadCount
to a high enough number, the height of the Column
will be large enough for the ScrollBar of the SingeChildScrollView
to appear. Then everything works fine.
However, if I set initialWidgetsLoadCount
to a lower number (e.g. 1), the Column
height will be too small for the ScrollBar to appear. Therefore the end of the SingeChildScrollView
cannot be reached by the user and no more items will be loaded.
I could set the initialWidgetsLoadCount
to a high enough number for my device, but there might be a device with a huge resolution.
So I'm tring to figure out a way to tell if the ScrollBar is there or not. And while the ScrollBar is not there yet I can keep loading the initialWidgetsLoadCount
of widgets.
Note: The Add-Button in this example is just for testing purposes! In a real environment I want the user to be able to load all items without having to use a FloatingActionButton
, even with very small values for initialWidgetsLoadCount
.
Nother Note: The size of the list is unknown. I keep fetching data until there is no more data.
Here is a DartPad of this example.
CodePudding user response:
I found a way using the ScrollController
. For this solution it does not matter whether you are using ListView.builder
or SingeChildScrollView
.
I changed the initState()
method and put the line that adds the initial widgets after the actual super.initState()
call:
@override
void initState() {
_scrollController.addListener(() {
if (_scrollController.position.maxScrollExtent ==
_scrollController.offset) {
//the bottom of the scrollbar is reached
//adding more widgets
_addTextWidgets(nextWidgetsLoadCount);
}
});
super.initState();
//after initState add the initial widgets
_addTextWidgets(initialWidgetsLoadCount);
_checkInitialExtent();
}
In the _checkInitialExtent()
I keep adding the initial widgets until the ScrollController
tells me that it is scrollable:
void _checkInitialExtent() {
//after the frame
WidgetsBinding.instance?.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
debugPrint(_scrollController.position.maxScrollExtent.toString());
//if the list is not scrollable yet
if (_scrollController.position.maxScrollExtent == 0) {
//add the initial widgets again
_addTextWidgets(initialWidgetsLoadCount);
//recursively check again
_checkInitialExtent();
} else {
//finally enough widgets to make it scrollable (maxScrollExtent > 0 now)
debugPrint("maxScrollExtent > 0 now");
}
}
});
}
CodePudding user response:
Any list has the end that you need to know that just calculate it inside the listener. Update your code with my example and test if matches your preferences.
Note: use
ListView.builder
for longer and larger lists.
/// The max list length.
const len = 200;
/// Decide the initial length.
const int initialWidgetsLoadCount = len < 15 ? len : 15;
/// Decide how many items you want to add.
const int nextWidgetsLoadCount = 15;
@override
void initState() {
// This is the initial fill request
_addTextWidgets(initialWidgetsLoadCount);
_scrollController.addListener(() {
// And listen to the list length to calculate the next items load length
if (_scrollController.position.maxScrollExtent ==
_scrollController.offset) {
if(counter < len) {
if((len - counter) < nextWidgetsLoadCount) {
_addTextWidgets(len - counter);
} else {
_addTextWidgets(nextWidgetsLoadCount);
}
}
}
});
super.initState();
}