Synchronize the scrolling of your ListViews in Flutter

Photo by Plann on Unsplash

Synchronize the scrolling of your ListViews in Flutter

In my current project, I have a requirement where I display multiple ListViews below each other and these lists must scroll together. It does not matter which list is scrolled by the user, the other one should follow it.

You can use ScrollController when you want to manage the scrolling of a ListView. My first idea was to use the same controller for each list, but that won't work, it will not update the list that is not scrolled.

Another option would be to implement the synchronization by listening to changes on the controllers. But there is a great package that could help us and it is made by Google developers, the linked_scroll_controller.

Let's see how we can use it.

Let's say we have 2 horizontal list views below each other.

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Awesome list 1'),
            SizedBox(
              height: 150,
              // First list
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) => _ListItem(
                  index: index,
                ),
              ),
            ),
            const Divider(),
            const Text('Awesome list 2'),
            SizedBox(
              height: 150,
              // Second list
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) => _ListItem(
                  index: index,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _ListItem extends StatelessWidget {
  const _ListItem({
    Key? key,
    required this.index,
  }) : super(key: key);
  final int index;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text('List item $index'),
        ),
      ),
    );
  }
}

This is how it looks like

scroll.gif

Let's synchronize our lists.

First we need to add linked_scroll_controller to our dependencies in pubspec.yaml:

dependencies:
  linked_scroll_controller: ^0.2.0

Then we need to create the scroll controller group, which will manage the synchronization for us.

class _MyHomePageState extends State<MyHomePage> {
  // Create a new scoll controller group
  late final LinkedScrollControllerGroup _controllerGroup =
      LinkedScrollControllerGroup();
  // Create 2 new scroll controllers in the group, so they are connected.
  late final ScrollController _controller1 = _controllerGroup.addAndGet();
  late final ScrollController _controller2 = _controllerGroup.addAndGet();

  // ...
}

Then we just need to add the 2 controllers to our ListViews.

// ...
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    const Text('Awesome list 1'),
    SizedBox(
      height: 150,
      child: ListView.builder(
        // Use the controller
        controller: _controller1,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) => _ListItem(
          index: index,
        ),
      ),
    ),
    const Divider(),
    const Text('Awesome list 2'),
    SizedBox(
      height: 150,
      child: ListView.builder(
        // Use the controller
        controller: _controller2,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) => _ListItem(
          index: index,
        ),
      ),
    ),
  ],
),
// ...

This is how it works:

scroll_sync.gif

That's it!

You can find the source code at github.com/dtengeri/linked_list_views_example