Flutter - Scrolling to a specific item in the ScrollView!🔥

LinXunFeng
5 min readAug 20, 2023

--

Pain points

Flutter officially provides ScrollController, which can scroll to the specified offset by calling the following two methods.

void jumpTo(double value)
Future<void> animateTo(
double offset, {
required Duration duration,
required Curve curve,
})

But the official does not provide the function of scrolling to the specified index item.

In order to solve the above problem, there are many third-party packages to implement this feature, the most famous of which is Google’s scrollable_positioned_list

And these packages all have some of the same problems:

  • Intrusive, have to use the Widget they provide to build the list view
  • GridView is not supported

In this regard, I decided to write a package (flutter_scrollview_observer) to realize this function and solve the above problems.

Advantage

  • It is low intrusive and will not limit your ScrollView implementation. It only requires the ScrollView to be a child of ViewObserver. If you don’t want to use it, just remove ViewObserver directly.
  • Based on [will not limit your list view implementation], flutter_scrollview_observer supports all ScrollView Widgets, such as: ListView, GridView, and even CustomSrollView.
  • Anti-shake, if the scrollable area at the end of the list is not enough to support scrolling to the corresponding position when scrolling to the specified index item, it will automatically scroll to the bottom to avoid the problem of rebound.

The following formally introduces the use of this package

Usage

  1. ListView

Create and use ScrollController instance normally

Here you can use ListView or SliverList of CustomSrollView

ScrollController scrollController = ScrollController();

ListView _buildListView() {
return ListView.separated(
controller: scrollController,
...
);
}

Create a ListObserverController instance and pass it to the ListViewObserver

ListObserverController observerController = ListObserverController(controller: scrollController);

ListViewObserver(
controller: observerController,
child: _buildListView(),
...
)

Note: When creating a ListObserverController instance, you need to pass in the ScrollController instance of the ListView. This step is very important, because the internal implementation principle of flutter_scrollview_observer is based on the jumpTo and animateTo methods provided by the official ScrollController

Now you can scroll to the specified index item

// Scroll to index item without animation
observerController.jumpTo(index: 100)

// Scroll to index item with animation
observerController.animateTo(
index: 100,
duration: const Duration(seconds: 1),
curve: Curves.ease,
);

2. GridView

Create and use ScrollController instance normally

Widget _buildGridView() {
return GridView.builder(
...
controller: scrollController,
...
);
}

Create a GridObserverController instance and pass it to the GridViewObserver

GridObserverController observerController = GridObserverController(controller: scrollController);

GridViewObserver(
controller: observerController,
child: _buildGridView(),
)

Now you can scroll to the specified index item

observerController.jumpTo(
index: 40,
);

observerController.animateTo(
index: 40,
duration: const Duration(seconds: 1),
curve: Curves.ease,
);

3. CustomSrollView

Support the mixed use of SliverList and SliverGrid!

As shown in the figure above, the slivers of CustomSrollView contain SliverList and SliverGrid

  • Clicking the first bottom right button scrolls to 29th itemof the SliverList
  • Clicking the second bottom right button scrolls to the 10th item of the SliverGrid

Create and use ScrollController instances normally

Widget _buildScrollView() {
return CustomScrollView(
controller: scrollController,
// scrollDirection: Axis.horizontal,
slivers: [
_buildSliverListView(),
_buildSliverGridView(),
],
);
}

Create your SliverList and SliverGrid normally and record their BuildContext

Widget _buildSliverListView() {
return SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
_sliverListCtx ??= ctx;
...
},
...
),
);
}

Widget _buildSliverGridView() {
return SliverGrid(
...
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
_sliverGridCtx ??= context;
...
},
...
),
);
}

Create a SliverObserverController instance and pass it to the SliverViewObserver

Note: Return the BuildContext of the two Slivers just recorded in sliverListContexts

SliverObserverController observerController = SliverObserverController(controller: scrollController);

SliverViewObserver(
controller: observerController,
child: _buildScrollView(),
sliverListContexts: () {
return [
if (_sliverListCtx != null) _sliverListCtx!,
if (_sliverGridCtx != null) _sliverGridCtx!,
];
},
...
),

Now you can scroll to the specified index item

observerController.animateTo(
sliverContext: _sliverListCtx,
index: 29,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);

observerController.animateTo(
sliverContext: _sliverGridCtx,
index: 10,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);

Explanation

  1. Selection of ViewObserver

It is recommended to use the corresponding ViewObserver:

  • If it is ListView or pure SliverList, it is recommended to use ListViewObserver
  • If it is GridView or pure SliverGrid, it is recommended to use GridViewObserver
  • In case of SliverList and SliverGrid, use SliverViewObserver

In this way, after getting the data model in the onObserve callback, there is no need to perform type judgment and conversion on it.

2. isFixedHeight

When the child widget is fixed height, please set isFixedHeight to true to improve performance!

observerController.jumpTo(
index: 100,
isFixedHeight: true
);

observerController.animateTo(
index: 100,
isFixedHeight: true,
duration: const Duration(seconds: 1),
curve: Curves.ease,
);

3. Whether the sliverContext needs to be passed

jumpTo({
required int index,
BuildContext? sliverContext,
bool isFixedHeight = false,
})

animateTo({
required int index,
required Duration duration,
required Curve curve,
BuildContext? sliverContext,
bool isFixedHeight = false,
})

As above, both the jumpTo and animateTo methods have a sliverContext parameter, which is used to specify which sliver performs the scrolling operation.

If you do not pass the sliverContext, it will be the first sliver in the ScrollView by default, and you only need to pass this value when you want the non-first sliver to scroll.

Realistic function

  1. Obtain the item information in the current viewport

As shown in the console in the above figure, the data of the item being displayed can be obtained in real time during the scrolling process.

2. Video list autoplay

This kind of function is very common. When the ListView is scrolled, the hit video in the specified area will be played automatically.

3. Scrolling to a specific item

Generally, there are functions in the details page. When the ListView scrolls, the top positioning navigation view will update the index. Clicking a index in the navigation view will locate the corresponding module.

--

--