Flutter - Scroll to Top & Bottom Examples

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.