Create a Responsive Animated Progress Bar in React Native

Jason Brown
InstructorJason Brown
Share this video with your friends

Social Share Links

Send Tweet
Published 7 years ago
Updated 6 years ago

In this lesson we'll show how to structure views to create a responsive progress bar (without defining a set width). It will allow for dynamic border, bar, and fill colors. We'll finish off by showing how to add animation to make the progress indicator smoothly transition.

[00:01] ProgressBar is just an easy way to show that something is happening inside of your application. We'll start by creating a progress bar. We'll say class progressBar extends component. We'll have a render method. Inside here, we'll just return a view.

[00:25] Now, in our main app, we're just going to set up a few things to show that this progress bar is responsive. First, we're going to say text load progress. Close that text out, then some more text saying 100 percent. Then we'll render our progress bar in the middle of all of this.

[00:46] For our progress bar, it will be composed of many properties. It'll be quite configurable. We'll do that via default props. We'll set up progressBar.defaultProps, send it an object. We'll just say the default height is 10 pixels, the border color will just default to black, the border width will default to two.

[01:10] The border radius, so the curve of the borders, will be four. Our bar color, we'll just say is tomato red. The fill color, which is the piece of the progress bar that's not filled in, we'll just say is RGBA 0005, so a light gray. Then also the duration of our animation eventually, we'll just say it defaults to 100.

[01:38] Now, we'll need to layer our views, and apply the appropriate styles. The first one will be our outer view. We'll say styles equal to an object. We'll say it's a flex direction row, which means that all of its children will go across the screen, which is what we want. We want our bar to go from left to right rather than from up to down.

[01:58] Then we'll also add in our height here. This height is then destructured from our props. We'll start saying const, and we'll just destructure all of these properties, because we'll use all of them. We'll say height, border color, border width, border radius, bar color, fill color, and that'll be all that we will need.

[02:24] Assign this. We'll destructure this from props. Because we are inside of a container that has a flex direction row, we need to add flex one to our outer container. This will then allow it to take up all available space.

[02:41] Once the text size and the text width here are calculated, this progress bar will then be automatically assigned an appropriate width. Now, we'll add our border view. Our border view will just be a view here. Close this, because this is where all of our other content will go.

[03:01] We'll say style is equal to...We'll also add a flex one here to say fill up all of the space inside. Border color, border width, and border radius. Now, if we refresh our emulator, we can see that we have text that goes across the screen, then we have our progress bar sitting in between both of the pieces of text, and it's automatically sized.

[03:26] Now, let's add the fill color for what's going to be behind the bar, AKA the percentage that is not filled. We can just set a view here. Say style equals an array. We'll use the stylesheet.absoluteFill, which is a helper method that does position absolute with left, top, right, and bottom of zero. Then we'll say background color is equal to our fill color.

[03:54] Now, when we refresh, we can see that our background color is filled in. Now, we'll add our bar in. We'll say animated.view, and we'll close that. We're doing the animated view, because we'll eventually want to animate the progress bar as it updates the percentage.

[04:13] We'll say style, and say position, absolute. We need to position it to the left of zero, the top and the bottom. The width will then be an animated percentage. However, we'll just set our background color to our bar color.

[04:35] The reason that we put this second is because of the way that views work when they're absolutely positioned. The bottom view of any child -- example, these two views are a child of this particular view -- this one will sit on top of this one. That way, whenever this animates, this view will be hidden, but still be appearing wherever there is not progress made.

[05:01] Now, we'll need to set up our animated progress value. We'll create a componentWillMount, and then say this.animation is equal to a new animated value. We'll pass in this.props.progress, which will just be a value between zero and one.

[05:19] Then in our component update function, we'll say if our previous props.progress doesn't equal this.props.progress -- it's just different -- that means we'll then call animated.timing, which will take our this.animation and a configuration object.

[05:40] In our case, our two value will be our new progress, so this.props.progress. Then our duration will be whatever's passed in, so this.props.duration. Then to start our animation, we need to call .start.

[05:57] Finally, we'll need to set a width for our bar. What we'll need to do is set up an interpolated value from our animation. Say const widthInterpolated is equal to this.animation.interpolate. This will receive an input range, which will be zero to one, which is just our progress that we're updating up above.

[06:21] The output range will be 0percent, and then 100 percent over here. We'll also want to set extrapolateClamp. That means that if anything goes beyond zero this way or above one that it will always stop at zero percent and never go beyond 100 percent. Otherwise, the view bar will then extend way beyond the screen if anything goes above one.

[06:50] Now, we'll take our widthInterpolated and pass it into our animated view bar. Say width is our widthInterpolated. We can come down to our application. Rather than a progress of zero to start, we'll say .5. Then we'll pass in progress equals this.state.progress.

[07:13] When we refresh, we can see that we are now at 50 percent progress. Now, let's turn this back to zero. We'll uncomment some of this code. What this does is just setInterval, and takes the previous state, and adds .1 to it every one second. Our progress bar will update and animate in 10 percent increments.

[07:36] Because 100 is a bit jarring, we'll switch our duration over to 500 milliseconds. Now, when we refresh, we can see that every second, it increases by .1, and does so in an animated, smooth fashion. Then once it reaches the end, our animation extrapolateClamp will kick in. This view will not extend beyond the 100 percent width that it's currently filling.

[08:06] There's one final piece, and that's if this particular view is not a flex direction row, but is flex direction column, which is the default for React Native. If we remove this flex direction row and refresh, we can see that this does not operate appropriately.

[08:24] To fix this, we need to add a row property to our progress bar to say that it's in a flex direction row, or the absence of row will mean it's in a column fashion. We don't want to apply our flex one on the outer regions.

[08:38] We'll remove our flex one, we'll destructure a row property, we'll add an array here, so we can add multiple styles, and say row. If we're in a flex direction row on the outside, then we will add flex one, otherwise it will just be undefined.

[08:58] With this undefined, it will just also say take up all available space with this flex direction row. We refresh, and we see that it is now taking up all of the space, and animating like we would expect. However, if we come back here, add flex direction row back, switch this to row, and refresh, now it's in a row fashion, and we've got our flex one to say take up the available space.

Samuel Suther
Samuel Suther
~ 6 years ago

I have a question about the animation of the progressbar. I have changed you code to jump from 0 to 70% like this:

componentDidMount(){
        this.setState(state => {
            return {
                progress: state.progress + 0.7,
            };
        });
}

I've expected an linear animation from 0 to 70%, but the animation is shown in three steps. (See animated Gif here: https://i.imgur.com/SYn9UHp.gif)

Did you have a hint for me, how to get an linear animation from 0 - 70 % without any stepping?

Found the solution:

Animation stuck on Emulator, on real Device, it works like a charm.

Markdown supported.
Become a member to join the discussionEnroll Today