Step 1: Adding Lottie Animation

For simplicity’s sake, I’ll have to assume you guys already know how to use a FlatList . For those who want to follow along, I’ve created a starter template for this tutorial. You can download it here. Don’t forget to run yarn install or npm install in the project after downloading it to make sure you get all the dependencies installed.

The first thing you’ll want to do is to get a Lottie animation. Head over Lottie Files, pick an animation that you like, and then download it to Lottie JSON format. For this tutorial I will be using this one:

Credits to Alex Martov: https://lottiefiles.com/9258-bouncing-fruits

After downloading your desired Lottie animation, open up the project folder, and move the animation file into the assets/ folder. Again, make sure the animation is a .json file! If not, go back to Lottiefiles and download the JSON version.

I’ve renamed my animation to “bouncing-fruits.json”

Now that you got the animation file, the next thing to do is to load it and display the animation. In order to do that, we’ll need to use lottie-react-native. Go ahead and install it by running yarn add lottie-react-native (or use npm install lottie-react-native )

Navigate to FruitList.jsx , right below the import statements, add the following:

Now inside styles let’s create another style called lottieView

And finally, for the exciting part, let’s wrap the existing FlatList and LottieView into a new View . Below is the full code for FruitList.jsx .

Now let’s run the app, and you’d probably see something like this

Bouncing fruits animation

Awesome! Except, the animation runs immediately when we open the app. It’s not exactly what we’re looking for… Let’s figure out how to actually turn this into a pull to refresh.

Step 2: “Hiding” animation behind the FlatList

The idea to achieve the pull to refresh effect is simple

Hide the animation behind the FlatList . As the FlatList scrolls down, track the changes in y-offset (how much did they scroll down), and animate the animation appropriately. When the user released the scroll view ( onResponderRelease ) check if the offset is enough to trigger a refresh. The animation remains visible during the entire duration of the refreshing. When refreshing ends, stops the animation and FlatList is then scrolled back up to hide the animation.

To hide the animation behind the Flatlist , all we have to do is go back to FruitList.jsx and update the styles for LottieView .

To those not following using the starter project, for this to work the FlatList must have a transparent background color (or no background color), and each row should have a background color. Only by doing so we can allow the animation to be visible when the FlatList is scrolled down.

Step 3: Tracking scroll offset

To track the scrolling offset, we’re going to make use of the onScroll prop, and then store that into a state called offsetY .

At the very top of FruitList.jsx , modify the import statement to include useState , and then create a new state called offsetY .

Create a function onScroll that receives a scroll event and then sets the y offset to state.

And then remember to assign the onScroll function to the onScroll FlatList prop.

And Voilà! We are now able to track the y offset of the Flatlist . Now let’s make use of the offset y, and then animate the animation according to the offset. Fortunately for us, LottieView provided us with the prop progress . Here’s what the documentation has to say:

progress: A number between 0 and 1, or an Animated number between 0 and 1. This number represents the normalized progress of the animation. If you update this prop, the animation will correspondingly update to the frame at that progress value. This prop is not required if you are using the imperative API.

Hmm… interesting. So progress is a percentage that determines the frame of the animation. Currently, we have the yOffset , and if you do console.log(yOffset) and scroll down, you’ll see something like this:

-0.5

-15.5

-75.5

-101.5

…

To convert that into a percentage (value between 0 to 1), we’ll also need the height where the refreshing happens.

Conveniently, we already have that. Take a look lottieView styles, and you’ll notice the height is set to 100 , which will be our height. Let’s refactor that by creating a new variable called refreshingHeight above the styles.

Now to get the progress, all we have to do it take the -offsetY / refreshingHeight

Right after the onScroll function, we’ll create the progress variable and assign it to LottieView prop.

If you run the app now, you should be able to scroll down and see that the animation follows your scrolling!

Step 4: On Refresh

Remember the plan that is detailed in Step 2? Here’s what we’ve done so far.

H̶i̶d̶e̶ ̶t̶h̶e̶ ̶a̶n̶i̶m̶a̶t̶i̶o̶n̶ ̶b̶e̶h̶i̶n̶d̶ ̶t̶h̶e̶ ̶ F̶l̶a̶t̶L̶i̶s̶t̶ .̶ A̶s̶ ̶t̶h̶e̶ ̶ F̶l̶a̶t̶L̶i̶s̶t̶ ̶s̶c̶r̶o̶l̶l̶s̶ ̶d̶o̶w̶n̶,̶ ̶t̶r̶a̶c̶k̶ ̶t̶h̶e̶ ̶c̶h̶a̶n̶g̶e̶s̶ ̶i̶n̶ ̶y̶-̶o̶f̶f̶s̶e̶t̶ ̶(̶h̶o̶w̶ ̶m̶u̶c̶h̶ ̶d̶i̶d̶ ̶t̶h̶e̶y̶ ̶s̶c̶r̶o̶l̶l̶ ̶d̶o̶w̶n̶)̶,̶ ̶a̶n̶d̶ ̶a̶n̶i̶m̶a̶t̶e̶ ̶t̶h̶e̶ ̶a̶n̶i̶m̶a̶t̶i̶o̶n̶ ̶a̶p̶p̶r̶o̶p̶r̶i̶a̶t̶e̶l̶y̶.̶ When the user released the scroll view ( onResponderRelease ) check if the offset is enough to trigger a refresh. The animation remains visible during the entire duration of the refreshing. When refreshing ends, stops the animation and FlatList is then scrolled back up to hide the animation.

Let’s start by creating another new state, isRefreshing . This will be used to track if the list is refreshing or not. Add the following below the offsetY state.

And then create the function onRelease right after the onScroll function. And then assign it to the FlatList prop onResponderRelease .

Step 5: Animation remains visible while refreshing

To make sure the animation is visible, we’ll add an empty ListHeaderComponent to the existing FlatList , with the paddingTop equivalent to the refreshingHeight .

Below isRefreshing , Create a new state extraPaddingTop . This will hold the padding value for our invisible ListHeaderComponent . And then in the onRelease function, set the extraPaddingTop to the refreshingHeight when it’s refreshing, and set it to 0 when it has finished refreshing.

We’re almost there! Running the app now will display something like this

A couple of things doesn’t seem right. The animation is not moving while it’s refreshing, and when the refresh is complete, the FlatList jumps abruptly back up. But don’t worry we’ll fix those things next.

Step 6: Animate while refreshing, and hides when refreshing ends

We’ll start by making the FlatList collapse graciously when the refreshing ended. But before that let’s refactor our code a little. Let’s use useEffect to update the other states whenever the isRefreshing is updated.

Now all we have to do in onRelease is to setIsRefreshing and our useEffect hook will automatically set the padding for us.

Now, let’s fix the issue with our animation. Create a new ref , and attach it to LottieView , this way we’ll be able to trigger the play function during refresh. Refer to the code below.

And finally, let’s make the FlatList collapse gracefully when the animation ends. To do that, we’ll make use of React Native’s Animated API, and update extraPaddingTop from a fixed value to an animated value.

And we’re done!

Your newly completed pull to refresh animation