positioned_scroll_observer 1.0.2 copy "positioned_scroll_observer: ^1.0.2" to clipboard
positioned_scroll_observer: ^1.0.2 copied to clipboard

An elegant scroll observer that support all official slivers could jump/animateToIndex for SliverList/Grid without breaking current widgets.

pub package GitHub Repo stars

Features #

  1. using jumpToIndex and animateToIndex to scroll to the specific index
  2. No breaking for your current sliver widgets, e.g., ListView/GridView, SliverList/SliverGrid/SliverAppBar
  3. support almost official RenderSliver that has single child or multi children

Getting started #

Use ScrollController #

  1. create a ScrollObserver
  final ScrollController _controller = ScrollController();
  late final ScrollObserver _observer =
      ScrollObserver.multiChild(itemCount: _itemCount);
  1. bind ScrollObserver with the item widget/builder that must be wrapped by ObserverProxy
    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,
    );
  1. use jumpToIndex/animateToIndex
_observer.jumpToIndex(
    index,
    position: _controller.position,
);

 _observer.animateToIndex(
    index,
    position: _controller.position,
    duration: const Duration(milliseconds: 200),
    curve: Curves.fastLinearToSlowEaseIn,
);

There you go

Use PositionedScrollController for ListView that only has a single RenderSliver #

  1. create PositionedScrollController
final PositionedScrollController _controller =
      PositionedScrollController.singleObserver();
  1. bind ScrollObserver to item widget/builder
    ListView.builder(
      controller: _controller,
      itemBuilder: (context, index) => ObserverProxy(
        observer: _controller.createOrObtainObserver(
            itemCount: _itemCount,
        ),
        child: ListTile(
          key: ValueKey<int>(index),
          leading: const CircleAvatar(
            child: Text("L"),
          ),
          title: Text("Positioned List Example $index"),
        ),
      ),
      itemCount: _itemCount,
    );
  1. use jumpToIndex/animateToIndex
_controller.jumpToIndex(index);

 _controller.animateToIndex(
    index,
    duration: const Duration(milliseconds: 200),
    curve: Curves.fastLinearToSlowEaseIn,
);

There you go

For ListView.custom and GridView.custom, you could also use PositionedChildListDelegate and PositionedChildBuilderDelegate for wrapping items in ObserverProxy conveniently

Usage #

  1. The item widget/builder must be wrapped using ObserverProxy
  2. ScrollObserver would observe all children for slivers, e.g., SliverList/SliverGrid, so all items should have the same ScrollObserver instead of creating a different ScrollObserver for each item.

Observing a single sliver #

  • if you want to use ScrollController directly, you could create a standalone ScrollObserver by using:

    1. ScrollObserver.singleChild for a sliver with a single child, such as SliverAppBar
    2. ScrollObserver.multiChild for a sliver with multi children, such as SliverList/SliverGrid
  • if you prefer using PositionedScrollController that would manage ScrollObserver created by you, you could create a controller by PositionedScrollController.singleObserver. Then, you could create a standalone ScrollObserver by using: PositionedScrollController.createOrObtainObserver:

    1. hasMultiChild indicates if this observer is for a sliver with multi children

Observing multiple slivers (typically for CustomScrollView that has multiple slivers) #

  • if using ScrollController, you have to create multiple ScrollObservers manually and bind them to different slivers. Each sliver should have an unique ScrollObserver that must adopt its type: single child or multi children

  • if using PositionedScrollController, you could create PositionedScrollController.multiObserver to manage multiple ScrollObservers automatically. Then, using PositionedScrollController.createOrObtainObserver to create a corresponding ScrollObserver for each sliver.

PositionedScrollController #

It has all methods of ScrollController by extends ScrollController and then help you to manage ScrollObserver.

  • PositionedScrollController.singleObserver manage only a single ScrollObserver that may have single child or multi children

  • PositionedScrollController.multiObserver manage multiple ScrollObserver that may have single child or multi children

  • createOrObtainObserver

parameter required default description
hasMultiChild YES true determine if the ScrollObserver is for a sliver that has multi children
itemCount No null the sliver's item count. if null, the observer would behave as a infinite scroll view
maxTraceCount NO null the maximum count when tracing ObserverProxy's ancestor RenderSliver and ParentData. Default to 50 internally, only setting it when you ensure you need to trace more nodes.
targetToRenderIndex NO null sometimes, the target index to which users want to scroll may not be same as the current render index. By using [targetToRenderIndex], users could define how to map the target index to a render index, e.g., ListView.separated/ReorderableListView. Users could set it on an instance of ScrollObserver not only when creating it. Setting it only when you ensure you need it.
renderToTargetIndex NO null same as targetToRenderIndex but in converting reversely.
  • jumpToIndex and animateToIndex. (should pass duration and Curve if using animateToIndex)
parameter required default description
index YES N/A the item's index for a sliver. No effects if ScrollObserver.hasMultiChild is false
whichObserver NO null the specific ScrollObserver that is observing a sliver. It is required if ScrollObserver.hasMultiChild is true
closeToEdge YES true try to scroll index at the leading edge if not over scrolling; otherwise, only ensure the index is visible on the screen.
  • isVisible: check if the given index is painted on the screen.
parameter required default description
index YES N/A the specify index you want to check visibility
whichObserver NO null the associated ScrollObserver with the index. Required if the controller is PositionedScrollController.multiObserver

ScrollObserver #

  • ScrollObserver.multiChild: create a ScrollObserver that observes a RenderSliver with multi children

  • ScrollObserver.singleChild: create a ScrollObserver that observes a RenderSliver with a single child

  • jumpToIndex and animateToIndex. (should pass duration and Curve if using animateToIndex)

parameter required default description
index YES N/A the item's index for a sliver. No effects if ScrollObserver.hasMultiChild is false
closeToEdge YES true try to scroll index at the leading edge if not over scrolling; otherwise, only ensure the index is visible on the screen.
position YES N/A the ScrollPosition attached to a ScrollController

My other packages #

18
likes
90
pub points
83%
popularity

Publisher

verified publishersimonwang.dev

An elegant scroll observer that support all official slivers could jump/animateToIndex for SliverList/Grid without breaking current widgets.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter

More

Packages that depend on positioned_scroll_observer