This tutorial shows you how to scroll to the top or bottom of a scrollable widget in Flutter.
If the content of a page doesn't fit into the screen, it's quite common to have a scrollable layout. Flutter provides some widgets that support scrollable content such as ListView
, GridView
, and SingleChildScrollView
. Sometimes, the content can be very long that the users need to take some time and effort to scroll to the top or bottom of the screen. In such a situation, providing buttons that allow the users to scroll to the very top or bottom can be very helpful. This tutorial shows you how to do it in Flutter.
Set ScrollController
Flutter has a class named ScrollController
, which can be used to control a scrollable widget. One of the usages is for animating the position to a particular offset. First, you have to create an instance of ScrollController
.
late final ScrollController _scrollController;
@override
void initState() {
_scrollController = ScrollController();
super.initState();
}
Then, you have to set the scrollable widget to use the controller. In this tutorial, we are going to use a ListView
widget. However, you can do the similar thing using other widgets that allow you to set the ScrollController
.
ListView.builder(
controller: _scrollController,
itemCount: 50,
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 50,
child: Card(
color: Colors.pinkAccent,
child: Center(
child: Text(
'Item: ${index + 1}',
style: const TextStyle(color: Colors.white),
),
),
),
);
},
)
Animates the Position
Using the ScrollController
instance, you can call the method below to animate the position to a given offset.
Future animateTo(
double offset,
{
required Duration duration,
required Curve curve,
}
)
The method requires you to pass the scroll offset. To scroll to the top, the offset should be set to _scrollController.position.minScrollExtent
whose value is typically 0.0
. To scroll to the bottom, you can set the offset to _scrollController.position.maxScrollExtent
.
The scroll animation can be set by using the optional named arguments. Pass a Duration
value as the duration
argument to set how long Flutter should perform the animation to the new offset. The animation curve can be set by passing a Curve
value as the curve
argument.
_scrollController.animateTo(
_scrollController.position.minScrollExtent, // Use maxScrollExtent to scroll to the bottom
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
Add Scroll to Top/Bottom Buttons
In this tutorial, we are going to add two floating action buttons, one for scrolling to the top, the other for scrolling to the bottom.
Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () {
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
},
backgroundColor: Colors.teal,
child: const Icon(Icons.arrow_upward),
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
},
backgroundColor: Colors.teal,
child: const Icon(Icons.arrow_downward),
),
],
),
),
// other arguments
);
If you want to enable or disable the buttons that scroll to top/bottom when the position reaches the minimum or maximum extent, you can add a listener to get the current position. The scroll to top button should only be enabled if the position is greater than the minimum extent. On the other hand, the scroll to bottom button should only be enabled if the position is lower than the maximum extent.
late final ScrollController _scrollController;
bool _enableToTopButton = false;
bool _enableToButtomButton = true;
@override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(() {
setState(() {
_enableToTopButton = _scrollController.offset > _scrollController.position.minScrollExtent;
_enableToButtomButton = _scrollController.offset < _scrollController.position.maxScrollExtent;
});
});
super.initState();
}
Full Code
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Woolha.com Flutter Tutorial',
home: MyPage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyPage extends StatefulWidget {
const MyPage({super.key});
@override
State<StatefulWidget> createState() {
return _MyPageState();
}
}
class _MyPageState extends State<MyPage> {
late final ScrollController _scrollController;
bool _enableToTopButton = false;
bool _enableToButtomButton = true;
@override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(() {
setState(() {
_enableToTopButton = _scrollController.offset > _scrollController.position.minScrollExtent;
_enableToButtomButton = _scrollController.offset < _scrollController.position.maxScrollExtent;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
backgroundColor: Colors.teal,
),
body: ListView.builder(
controller: _scrollController,
itemCount: 50,
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 50,
child: Card(
color: Colors.pinkAccent,
child: Center(
child: Text(
'Item: ${index + 1}',
style: const TextStyle(color: Colors.white),
),
),
),
);
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
if (!_enableToTopButton) {
return;
}
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
},
backgroundColor: _enableToTopButton ? Colors.teal : Colors.grey,
child: const Icon(Icons.arrow_upward),
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: () {
if (!_enableToButtomButton) {
return;
}
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.fastLinearToSlowEaseIn
);
},
backgroundColor: _enableToButtomButton ? Colors.teal : Colors.grey,
child: const Icon(Icons.arrow_downward),
),
],
),
),
);
}
}
Summary
To scroll to the top or bottom of a scrollable widget in Flutter, you can utilize a ScrollController
which has to be set as the controller of the scrollable widget. Call the animateTo
method to programmatically animate the position to a particular offset. The position.minScrollExtent
value of the controller can be used to get the minimum offset for scrolling to the top. Meanwhile, for scrolling to the bottom, use the value of position.maxScrollExtent
as the new offset.