Search code examples
flutterdartuser-experience

How can I implement this specific scroll animation using Flutter?


I am trying to implement this scroll animation (GIF below). But, after extensive searching online, I haven't found any specific information to this problem. I looked at the ScrollPhysics class but I don't think that's the right path. My idea is to have transparent separators in between widgets and then to change the height of each separator to create the desired "illusion". I would appreciate any suggestions on how to implement this thing.

enter image description here


Solution

  • Update:

    Here's a full example on DartPad that you can try yourself: https://dartpad.dev/26671eeec673a663b26c5dda68c934c2

    Original:

    Here's a simple example on how to approach this using NotificationListener:

    class Example extends StatefulWidget {
      @override
      _ExampleState createState() => _ExampleState();
    }
    
    class _ExampleState extends State<Example> {
      double spaceBetween = 10.0;
      final _duration = Duration(milliseconds: 200);
    
    
      _onStartScroll(ScrollMetrics metrics) {
        // if you need to do something at the start     
      }
    
      _onUpdateScroll(ScrollMetrics metrics) {
        // do your magic here to change the value
        if(spaceBetween == 30.0) return;
        spaceBetween = 30.0;
        setState((){});
      }
    
      _onEndScroll(ScrollMetrics metrics) {
        // do your magic here to return the value to normal
        spaceBetween = 10.0;
        setState((){});
      }
    
      @override
      Widget build(BuildContext context) {
        return NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            if (scrollNotification is ScrollStartNotification) {
              _onStartScroll(scrollNotification.metrics);
            } else if (scrollNotification is ScrollUpdateNotification) {
              _onUpdateScroll(scrollNotification.metrics);
            } else if (scrollNotification is ScrollEndNotification) {
              _onEndScroll(scrollNotification.metrics);
            }
            return true; // see docs
          },
          child: ListView(
            children: [
              Container(height: 100, width: 100, color: Colors.red),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.blue),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.red),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.blue),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.red),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.blue),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.red),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.blue),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.red),
              AnimatedContainer(duration: _duration, height: spaceBetween),
              Container(height: 100, width: 100, color: Colors.blue),
              AnimatedContainer(duration: _duration, height: spaceBetween),
            ],
          ),
        );
      }
    }
    

    I used AnimatedContainer here but you can use explicit animations if you want too.