This tutorial shows you how to use ScrollController
in Flutter.
It's very common to have a scroll view if the content to be displayed cannot fit into the screen. Creating such a view can be done in Flutter as well. Sometimes, you may need to do additional things, such as handling when the user performs a scroll or getting the current scroll offset. For those purposes, you can use a built-in class in Flutter named ScrollController
.
Using ScrollController
ScrollController
is a controller for a scrollable widget. It's usually stored as member variables in State
classes and can be reused every time the build
method is called. It can be used in multiple scrollable widgets, though you can't do it for certain uses. Below is the constructor of ScrollController
.
ScrollController({
double initialScrollOffset = 0.0,
bool keepScrollOffset = true,
String? debugLabel,
void Function(ScrollPosition)? onAttach,
void Function(ScrollPosition)? onDetach,
})
There is no required argument. For the basic usage, we are going to create an instance of it without passing any argument. You have to pass it as the argument of a widget that accepts a ScrollController
as the argument. There are some widgets having that type of argument such as ListView
, GridView
, and CustomScrollView
.
Set ScrollController
of a Widget
In this tutorial, we are going to set the ScrollController
of a ListView
. First, create the instance and store it as a member variable of a State
class. The ListView
widget has an argument named controller
. You need to pass the instance as the controller
argument. The usage for other types of widget is very similar.
class MyPageState extends State<MyPage> {
late final ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView.separated(
controller: _controller,
itemCount: 50,
itemBuilder: (_, int index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: Text('Item $index'),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider(
indent: 20,
endIndent: 20,
thickness: 2,
);
},
),
),
],
);
}
}
A ScrollController
instance can be used by more than one scroll view. That means you can pass the same instance to be the controller of multiple widgets. However, there are some limitations which will be explained later.
Set Initial Offset
By default, Flutter will display the uppermost content initially. In other words, the initial offset is 0. To set the initial offset, you can pass an argument named initialScrollOffset
to the constructor of ScrollController
.
ScrollController(
initialScrollOffset: 200.0,
// other arguments
);
Handle Restoring Offset
By default, Flutter can save the current scroll offset using PageStorage
and restore it when the controller's scrollable is recreated. You can change that behavior by passing a boolean value as the keepScrollOffset
argument which defaults to true
. If you change it to false
, Flutter won't remember the scroll offset and thus resetting the offset when the scrollable is recreated.
ScrollController(
keepScrollOffset: false,
// other arguments
);
Get Current Scroll Offset
The current scroll offset can be obtained by accessing the offset
property of the ScrollController
.
double currentOffset = _controller.offset;
However, if you want to access the offset
property, you can only pass it to one scroll view. Otherwise, if it's passed to multiple scroll views, you'll get the following error.
The following assertion was thrown while dispatching notifications for ScrollController:
ScrollController attached to multiple scroll views.
'package:flutter/src/widgets/scroll_controller.dart':
Failed assertion: line 156 pos 12: '_positions.length == 1'
Get Current ScrollPosition
To get more detailed information, you can access a ScrollPosition
object by accessing the position
property. Like the offset
property, it can only be accessed if there is only one scroll view.
ScrollPosition currentScrollPosition = _controller.position;
Below is the output example.
ScrollPositionWithSingleContext#ff02a(offset: 448.0, range: 0.0..2076.6, viewport: 507.4, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, BallisticScrollActivity#041e1(AnimationController#51ec4(⏭ 447.965; paused; for BallisticScrollActivity)), ScrollDirection.reverse)
Animate to Offset
ScrollController
also allows you to animate the current scroll position to a particular position programmatically. It can be done by calling the animateTo
method. It has one positional argument which indicates the offset to scroll to. There are also optional named arguments which can be used to customize the duration and curve of the animations.
Future animateTo(
double offset,
{
required Duration duration,
required Curve curve,
}
)
Below is an example that sets the offset to 100 with an animation duration of 500 milliseconds and a fastLinearToSlowEaseIn
animation curve.
_scrollController.animateTo(
100,
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
ScrollController
Parameters
double initialScrollOffset
: The initial offset value. Defaults to0.0
.bool keepScrollOffset
: Whether to save the current offset withPageStorage
each time a scroll completes and restore it if the scrollable is recreated. Defaults totrue
.String? debugLabel
: A label to be used as the output oftoString
.void Function(ScrollPosition)? onAttach
: Function to be called when aScrollPosition
is attached to the controller.void Function(ScrollPosition)? onDetach
: Function to be called when aScrollPosition
is detached from the controller..
Summary
That's how to use ScrollController
in Flutter. You can pass an instance of it to certain widgets with scroll support. Then, you can use it to control the scroll position and get the current offset position.
You can also read about: