See the live example of
SuperSliverList
.
SuperSliverList
and SuperListView
are drop in replacement widgets for SliverList
and ListView
with greatly improved performance and additional features:
SliverList
performance degrades heavily when quickly scrolling through a large amount of items with different extents, requiring workarounds such as using FixedExtentSliverList
or prototype items. SuperSliverList
uses different layout algorithm and can handle virtually unlimited number of items with variable extents without any slow-downs.
SliverList
does not provide any way to jump or animate a particular index. There is a scrollable_positioned_list package that provides this functionality, but it comes at a cost, as it requires custom scroll view, does not seem to work properly with Scrollbars, can't be used with with other slivers (such as sticky headers) and ultimately is backed by a SliverList
so it has the same performance issues as mentioned above.
SuperSliverList
provides a way to reliably jump and animate to a specific item, even if the item is outside of the viewport and has not been built or laid out yet.
SliverList
is quite prone to scrollbar erraticaly jumping around when scrolling through a list of items with different extents. With SuperSliverList
the scrollbar should behave more predictably. See the Advanced section for more details.
SuperListView
is a drop-in replacement for ListView
, and as such you can use it same way you'd use ListView
:
SuperListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
)
SuperSliverList
is a drop-in replacement for SliverList
and should work with any CustomScrollView
configuration:
CustomScrollView(
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: SuperSliverList(
delegate: SliverChildListDelegate(
<Widget>[
const Text("I'm dedicating every day to you"),
const Text('Domestic life was never quite my style'),
const Text('When you smile, you knock me out, I fall apart'),
const Text('And I thought I was so smart'),
],
),
),
),
],
)
ListController
can be provided to SuperSliverList
/SuperListView
and used to jump or animate to specific item:
class _MyState extends State<MyWidget> {
final _listController = ListController();
final _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return SuperListView.builder(
listController: _listController,
controller: _scrollController,
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
);
}
void jumpToItem(int index) {
_listController.jumpToItem(
index: index,
scrollController: _scrollController,
alignment: 0.5,
);
}
void animateToItem(int index) {
_listController.animateToItem(
index: index,
scrollController: _scrollController,
alignment: 0.5,
// You can provide duration and curve depending on the estimated
// distance between currentPosition and the target item position.
duration: (estimatedDistance) => Duration(milliseconds: 250),
curve: (estimatedDistance) => Curves.easeInOut,
);
}
}
Very roughtly speaking SuperSliverList
works by estimating the extent of items that are outside of viewport and when these items are scrolled into the viewport cache area the scroll position is transparently adjusted to account of the difference between estimated and actual extents. On small lists this difference may result in scrollbar movement not being perfectly aligned with list movement. SuperSliverList
provides two ways to rectify this:
You can register custom callback that will be used to estimate extent of estimated items. This can be useful if you have an idea, atleast approximately, how large the extent of each item is.
SuperSliverList(
delegate: /*...*/,
estimateExtent: (index) => 100.0, // Provide your own extent estimation
)
SuperSliverList
can, if needed, asynchronously precalculate extents for items. To enfore this, subclass ExtentPrecalculationPolicy
and provide it to SuperSliverList
:
In this example the extents are eagerly precalculated for lists with less than 100 items:
class MyPrecalculationPolicy extends ExtentPrecalculationPolicy {
@override
bool shouldPrecaculateExtents(ExtentPrecalculationContext context) {
return context.numberOfItems < 100;
}
}
return SuperSliverList(
delegate: /*...*/,
extentPrecalculationPolicy: myPolicy,
)
The threshold is arbitrary, but in general there are diminishing returns for precalculating extents for large lists, as the extent estimation error for each item has much smaller impact on the scrollbar position if there are many items.
See the example folder for a complete sample app using SuperSliverList
. You can also see the example deployed live.