Scrolling Animation in Flutter

Flutter’s animation library is really Awesome for creating really complex animations however there are times where you would want to use more subtle animation such as those which occur whenever the user does something in your app.

Today I will talk about one such animation, scrolling animation. I will be covering how to make smooth scrolling animations in Flutter along with a few real world examples.

Making sense of Scrolling Animation

There are two types of Scrolling Animation the first is a normal animation that occurs when the user scroll to a specific place and the second depends on the overall amount of scrolling; the value of the animation depends on how much the user scrolls.


Drive layout based on the scroll position

Every scrollable widget in Flutter has some sort of controller that implement ScrollController. In the next example I will use the Controller to read the current scroll offset of the widget then use that value to drive a visual response.

Here is an example of what we will be doing:

Declare the following variables in the State object /class:

NOTE: This example will work just if you know how many items are in the list .

Next, create the layout :

Time for the controller :

The only interesting part is line 11 where we have the math to calculate the width for the green Container. We are getting the user’s current offset in the list and dividing it by the total length of the screen. This value is then divided by the height of the items multiplied by the number of items in the list. and finally, you should remove the listeners in dispose method :

This is all the code necessary to implement any type of animation based on the scroll position. However, after I published the article Simon Lightfoot mentioned a better way to do this and even with less code just using AnimatedBuilder to listen to the controller

Using this way you don't have to add or remove listeners to the controller.


Trigger Animation based on the scroll events

Imagine a scenario where you want to hide some widgets when the user scrolls up/down with some in/out animations. In this case, you don’t want to check every time the user scrolls instead what you might want is a way to know if the user starts/stopped scrolling.

Luck for us, there is no better widget for this than NotificationListener. Notification Listener is another widget provided by Flutter which allows you to wrap a scrollable. It allows us to listen for ScrollNotifications whenever the scrollable is scrolled.

Example: Hide widgets(AppBar, FAB, and BottomAppBar) when the user is scrolling and show them when they stop:

Hide Widgets when the user is scrolling and show them when he stops

Step 1: Setup Layout

NOTES:

  1. Using PreferredSize in the appBar is required otherwise you should create your custom appBar
  2. I used FittedBox just to avoid some scaling issues while animating. Delete it and you will see. that the Icon in the FAB will not scale

Step 2: Setup Animation

The boilerplate is really simple but just to be clear, I am going to use a Tween(begin: 0.0, end: 50.0) for the animation object and change the hardcoded value of 50.0 in all the previous code to use aniamtion.value instead:

And if you are asking why I am using PreferredSize here the answer is simple you can’t edit the default AppBar height without making a custom one, and for the bottomNavigationBar you can use any widget.

Step 3: Listen to Scroll Notifications

Wrap the ListView with NotificationListener of Type ScrollStartNotification and another one for ScrollEndNotification

Let me explain: If the user starts scrolling then the animation is reversed which means hide the widgets or make them smaller so they will become invisible. If the user stops moving then we show the widgets again by playing the animation.

The previous example is good but it’s not good UI practice. Let’s implement a common UI pattern, a “Showing back-to-top” button for when the user is scrolling down.

In this example, we will do the same Step 1 and 2 from the previous example, however, we will make some changes to step 3:

We use ScrollUpdateNotification to get the scrollDelta. This allows us to decide the direction in which the user is scrolling while also making it easier for us to inverse the effect. For example, if you want to show the widget's if the user is scrolling down just inverse the condition from notification.scrollDelta < 0 to notification.scrollDelta > 0 and vice versa for the other condition.

If you don’t like this method, an alternative would be to listen to UserScrollNotification to obtain the ScrollDirection. If the user stops scrolling you will get a ScrollDirection.idle

Important note on ScrollNotification

Don’t drive your layout based on ScrollUpdateNotifications. When the notification is received, the layout phase is already complete. However, you can still do painting effects since painting happens after layout.


Animate dynamic layout using LayoutBuilder

Every scrollable widget in Flutter is built on top of Slivers. When it comes to Slivers, we must use aCustomScrollView which means you have only one controller for all your slivers. The problem is some sliver behavior is complex and you can't get the right numbers by just using a ScrollController.

For example: SliverAppBar with pinned:true and floating:true. As an alternative, we can use a LayoutBuilder to get the box contains or the amount of space available.

Let's try to simulate the next example :

Step 1: Basic Layout

Step 2: use LayoutBuilder

Wrap the Stack in line 9 with LayoutBuilder and implement the Builder function. Once you are finished, create two new double as shown in the code below:

Step 3: Animate The Widgets


Complex Example

So for dinner today, you are going to make this:

For this one I will have the full source code linked below along with some notes. Try to attempt this on your own, I look forward to seeing the results.

Note 1: I used the same Setup from the previous example but i have two layers here the first contains the Text and the Icon in the right and the second layer contains the CustomPainter

Note 2: The CustomPaint contains the majority of the setup: Before the and after the animation crosses 50%, we draw just the line. For the remaining we draw both the Arc and the Polygon

Note 3: it’s just simple math I didn’t use any formula I was just testing to get the right numbers and I think I it can be improved in the future

Finally here is the code source for the last animation this is still a WIP but if you have any suggestions that would be great, see you in the next article 🙋‍♀️

For more articles don’t forget to follow me and Flutter Community on medium and Twitter.

Check out some of my other articles: https://medium.com/flutter-community/@rahiche