Flutter - Scrolling to a specific item in the ScrollView!🔥
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
- 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
- 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
- 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.
Github: LinXunFeng/flutter_scrollview_observer
Series
- Flutter — Getting the items information those are currently displaying in ScrollView
- Flutter — Scrolling to a specific item in the ScrollView!🔥
- Flutter — Quickly implement the effect of the chat session list, perfect 💯
- Flutter — New upgrade😱Supports observing ScrollView built by third package💪
- Flutter — Play alternately waterfall flow video 🎞
- Flutter — Keep IM message position greatly upgraded (supports generative messages like ChatGPT) 🤖
- Flutter — Anti-occlusion of form in ScrollView 🗒
- Flutter — Quickly achieve half-view exposure statistic 📊
- Flutter — How to quickly implement an contact list page (azlist) 📓
- Flutter — Supports observing NestedScrollView, with greater compatibility 😈