Search code examples
flutterdart

Can a scrollController.animateTo be cancelled or interrupted in the middle of its scroll?


Can a scrollController.animateTo be cancelled or interrupted in the middle of its scroll?

For example, lets say I have a listview and I want to animateTo the bottom over the course of 10 seconds:

_scrollController.animateTo
(
   _scrollController.position.maxScrollExtent,
    duration: Duration(seconds: 10),
    curve: Curves.linear
);

But 5 seconds into this, some event occurs, say a state change, so I want to immediately cancel the scroll wherever it happens to be. Is this possible?


Solution

  • I'm not sure if there is a better solution but this seems to work.

    You can call animateTo again, except with the current position as the destination, and 0 seconds as the duration.

    You can try with this demo below:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
        const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: ScrollDemoWidget(),
        ),
      );
    }
    
    class ScrollDemoWidget extends StatefulWidget {
      const ScrollDemoWidget({Key? key}) : super(key: key);
    
      @override
      _ScrollDemoWidgetState createState() => _ScrollDemoWidgetState();
    }
    
    class _ScrollDemoWidgetState extends State<ScrollDemoWidget> {
      final ScrollController _scrollController = ScrollController();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Scroll Demo'),
            actions: [
              IconButton(
                icon: const Icon(Icons.arrow_upward),
                onPressed: () {
                  double current = _scrollController.position.pixels;
                  double extent = _scrollController.position.maxScrollExtent;
                  double millis = 10 * 1000 * (current / extent);
    
                  _scrollController.animateTo(0,
                      duration: Duration(milliseconds: millis.round()),
                      curve: Curves.linear);
                },
              ),
              IconButton(
                icon: const Icon(Icons.arrow_downward),
                onPressed: () {
                  double current = _scrollController.position.pixels;
                  double extent = _scrollController.position.maxScrollExtent;
                  double millis = 10 * 1000 * (1 - (current / extent));
    
                  _scrollController.animateTo(
                      _scrollController.position.maxScrollExtent,
                      duration: Duration(milliseconds: millis.round()),
                      curve: Curves.linear);
                },
              ),
              IconButton(
                icon: const Icon(Icons.stop),
                onPressed: () {
                  _scrollController.animateTo(_scrollController.position.pixels,
                      duration: const Duration(seconds: 0), curve: Curves.linear);
                },
              ),
            ],
          ),
          body: ListView.builder(
            controller: _scrollController,
            itemCount: 100,
            itemBuilder: (context, i) => ListTile(title: Text('Tile $i')),
          ),
        );
      }
    }