Features
-
Using
jumpToIndex
andanimateToIndex
to scroll to the specificindex
-
Jumping/animating to the position by specifying a ratio in a scroll view. See how to align the render object. That would be very useful if you want to quickly jump to the top, middle or end of a list/grid.
-
Using
PositionRetainedScrollPhysics
to retain the old offset to avoid scrolling when adding new items into the top ofListView
. See retain old scroll offset. -
Check if the specific
index
is visible on the screen. See check visibility. -
Check which items are visible in the viewport. See get visible items
-
Check the visible ratio of the observed
RenderObject
in a viewport. See how to use it in a GroupList -
No breaking for your current sliver widgets, e.g.,
ListView
/GridView
,SliverList
/SliverGrid
/SliverAppBar
, just wrapping your item widgets usingObserverProxy
. Supported:
x
ListViewx
GridViewx
CustomScrollViewx
SingleChildScrollViewx
ListWheelScrollView
Getting started
-
First, creating and binding the observer to all items. (See box scroll observer and sliver scroll observer)
-
then, using the observer like:
_observer.jumpToIndex( index, position: _controller.position, ); _observer.animateToIndex( index, position: _controller.position, duration: const Duration(milliseconds: 200), curve: Curves.fastLinearToSlowEaseIn, );
Usage for scroll views that do not rely on RenderSliver
, e.g., SingleChildScrollView
and ListWheelScrollView
-
create a
BoxScrollObserver
for observing the box with multi children.final ScrollController _controller = ScrollController(); late final _observer = ScrollObserver.boxMulti( axis: _axis, itemCount: 30, );
-
bind the observer to the box's children. (Using
ObserverProxy
to wrap each item).SingleChildScrollView( controller: _controller, scrollDirection: _axis, child: Column( children: [ for (int i = 0; i < 30; i++) ObserverProxy( observer: _observer, child: DecoratedBox( decoration: BoxDecoration(border: Border.all()), child: SizedBox( height: 100, width: 100, child: Center( child: Text("Column item $i"), ), ), ), ), ], ), );
Usage for slivers widgets.
-
create a
SliverScrollObserver
for observing the sliver with multi children.final ScrollController _controller = ScrollController(); late final _observer = ScrollObserver.sliverMulti(itemCount: 30);
-
bind the observer to each item for the sliver.
ListView.builder( controller: _controller, itemBuilder: (context, index) => ObserverProxy( observer: _observer, child: ListTile( key: ValueKey<int>(index), leading: const CircleAvatar( child: Text("L"), ), title: Text("Positioned List Example $index"), ), ), itemCount: _itemCount, );
For
ListView.custom
andGridView.custom
, you could also usePositionedChildListDelegate
andPositionedChildBuilderDelegate
for wrapping items inObserverProxy
conveniently
Usage
For observing slivers:
- observing a sliver with single child, using
ScrollObserver.sliverSingle
to create. - observing a sliver with multi children, using
ScrollObserver.sliverMulti
to create.
For observing other scroll views that have no sliver descendants.
- observing a box with single child, using
ScrollObserver.boxSingle
to create. (rare cases and need more testing) - observing a box with multi children, using
ScrollObserver.boxMulti
to create.
Checking index
is visible on the screen
get visible items
More details, see API reference.
Using PositionRetainedScrollPhysics
for retaining the old scroll offset
ListView.builder(
controller: _controller,
reverse: true,
physics: const PositionRetainedScrollPhysics(),
itemBuilder: (context, index) => _items[index],
itemCount: _itemCount,
);
Jump/animate to a ratio position in a viewport
_observer.showInViewport(
_controller.position,
alignment: 0.5,
);
By setting different alignment
, you could jump/animate to the position according to the ratio: alignment
.
- for
alignment = 0.0
, it would align the render object' leading to the leading of the viewport's main axis extent. - for
alignment = 0.5
, it would align the render object's center to the center of the viewport;s main axis extent. - for
alignment = 1.0
, it would align the render object's trailing to the trailing of the viewport's main axis extent.
you could also specify
alignment
as the number between[0, 1]
Pay attention
- The item widget/builder must be wrapped using
ObserverProxy
- All observers would
normalizeIndex
to ensure theindex
is in a valid range determined byitemCount
of observers, so developers should also update observers'itemCount
when the scroll views' item count changes. - Items that have the same
RenderObject
observed by an observer should share the same observer instance, instead of creating different observers for each item. - When using
ScrollObserver.boxMulti
,axis
is required so that the observer could estimate the scroll offset along the correct main axis.
Examples:
- ListView example
- GridView example
- CustomScrollView example
- ReorderableListView example
- ListWheelScrollView example
- SingleChildScrollView example
- GroupList example
FAQ
TODO
Contributions
Feel free to contribute to this project.
If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.
If you fixed a bug or implemented a feature, please send a pull request