How to link multiple Scrollable widgets in Flutter

How can you control two Scrollable widgets with one ScrollController in Flutter? In short, you can’t. However, it is possible to link multiple Scrollables together. This article will demonstrate how to do this.

If a ScrollController is attached to multiple Scrollable widgets and used, this error occurs:

======== Exception caught by foundation library ====================================================
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 158 pos 12: '_positions.length == 1'

To get around this, when the position (offset) of one ScrollController changes, the position of the other ScrollController is updated and vice-versa. This ensures that both Scollables are always at the same position. This is particularly useful when we want to juxtapose two ordered lists of similar objects to compare them.

The code implementation for linking the controllers is shown below:

  final ScrollController list1Controller = ScrollController();
  final ScrollController list2Controller = ScrollController();

  @override
  void initState() {
    super.initState();
    list1Controller.addListener(controller1Listener);
    list2Controller.addListener(controller2Listener);
  }

  void controller1Listener() {
    if (list1Controller.offset != list2Controller.offset) {
      setState(() {
        list2Controller.jumpTo(list1Controller.offset);
      });
    }
  }

  void controller2Listener() {
    if (list1Controller.offset != list2Controller.offset) {
      setState(() {
        list1Controller.jumpTo(list2Controller.offset);
      });
    }
  }

  @override
  void dispose() {
    list1Controller.removeListener(controller1Listener);
    list2Controller.removeListener(controller2Listener);
    list1Controller.dispose();
    list2Controller.dispose();
    super.dispose();
  }

In my example, the `word_generator` library is used to generate two similar lists and two ListViews are used to compare the lists side-by-side. The lists are generated as shown:


  final wordGenerator = WordGenerator();

  late final List<String> list1;
  late final List<String> list2;

  @override
  void initState() {
    super.initState();
    const int listLength = 200;
    list1 = List.generate(listLength, (_) => wordGenerator.randomVerb());
    list2 = List.generate(
      listLength,
      (index) => '${list1[index]} ${wordGenerator.randomNoun()}',
    );
  }

The result is shown below:

Check out the complete example at https://github.com/LordOlumide/linked_scrollcontrollers_example.

In this article, we explored how to link two ScrollController instances to synchronize the scrolling of two scrollable widgets in Flutter. While a single ScrollController cannot be attached to multiple scroll views directly, we can achieve synchronization by updating the offset of one controller whenever the other changes. By implementing listeners on both controllers and using “jumpTo“ to update their positions, we ensure that both scroll views stay in sync. This technique is particularly useful for scenarios like comparing two lists side-by-side. Feel free to adapt this technique to your own projects. Happy coding!