positioned_scroll_observer 2.2.2 positioned_scroll_observer: ^2.2.2 copied to clipboard
An elegant scroll observer that support most scroll views could jump/animateToIndex without breaking current widgets.
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:
- ✅ ListView
- ✅ GridView
- ✅ CustomScrollView
- ✅ SingleChildScrollView
- ✅ ListWheelScrollView
- ❌ NestedScrollView (waiting testing)
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