Search code examples
scrolldartcontrollerfluttersynchronization

Scroll multiple scrollable widgets in sync


to put it simply:

is there a way to have multiple sccrollable widgets (say, SingleSchildScrollView) together in sync?


i just want 2 scrollables that can scroll the other as i scroll one.

this way i can use Stack to put them on top of each other and the one behind can scroll following the front one.

or maybe put them in another set of Column or Row so that they are separate, but still scrolls by just scrolling either one.

i tried using controller but it does not seems to be doing what i think it is.


Try the code below for example, the "RIGHT" will be in front of the "LEFT" and if i try to scroll them, only the RIGHT will move. so how do i move them both together at the same time??

please dont tell me to put the stack inside a ListView, that is not what i need.

class _MyHomePageState extends State<MyHomePage> {

  final ScrollController _mycontroller = new ScrollController();

  @override
  Widget build(BuildContext context) {
    body:
      Container(
        height: 100,
        child:
          Stack( children: <Widget>[
            SingleChildScrollView(
              controller: _mycontroller,
              child: Column( children: <Widget>[
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
                Text('LEFT            '),
              ],)
            ),
            SingleChildScrollView(
              controller: _mycontroller,
              child: Column(children: <Widget>[
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
                Text('          RIGHT'),
              ],)
            ),
          ])
      )
}}

i believe this question has been asked before in multiple forums before but nobody has put a conclusion or solution to this at all. (see here)


Solution

  • i managed to sync multiple scrollables by using their offset, utilizing their ScrollNotification.

    here's a rough code example:

    class _MyHomePageState extends State<MyHomePage> {
    
      ScrollController _mycontroller1 = new ScrollController(); // make seperate controllers
      ScrollController _mycontroller2 = new ScrollController(); // for each scrollables
    
      @override
      Widget build(BuildContext context) {
        body:
          Container(
            height: 100,
            child: NotificationListener<ScrollNotification>( // this part right here is the key
              Stack( children: <Widget>[
    
                SingleChildScrollView( // this one stays at the back
                  controller: _mycontroller1,
                  child: Column( children: <Widget>[
                    Text('LEFT            '),
                    Text('LEFT            '),
                    Text('LEFT            '),
                    Text('LEFT            '),
                    Text('LEFT            '),
                    Text('LEFT            '),
                  ],)
                ),
                SingleChildScrollView( // this is the one you scroll
                  controller: _mycontroller2,
                  child: Column(children: <Widget>[
                    Text('          RIGHT'),
                    Text('          RIGHT'),
                    Text('          RIGHT'),
                    Text('          RIGHT'),
                    Text('          RIGHT'),
                    Text('          RIGHT'),
                  ],)
                ),
              ]),
    
              onNotification: (ScrollNotification scrollInfo) {  // HEY!! LISTEN!!
                // this will set controller1's offset the same as controller2's
                _mycontroller1.jumpTo(_mycontroller2.offset); 
    
                // you can check both offsets in terminal
                print('check -- offset Left: '+_mycontroller1.offset.toInt().toString()+ ' -- offset Right: '+_mycontroller2.offset.toInt().toString()); 
              }
            )
          )
    }}
    

    basically each SingleChildScrollView has its own controller. each controller has their own offset values. use the NotificationListener<ScrollNotification> to notify any movement, anytime they are scrolled.

    then for each scroll gesture (i believe this is a frame by frame basis), we can add jumpTo() command to set the offsets in anyway we like.

    cheers!!

    PS. if the list has different length, then the offset will be different and you will get a stack overflow error if you try to scroll past its limit. make sure to add some exceptions or error handling. (i.e. if else etc.)