BMI Calculator in Flutter – Part 7 – Animated transition
Hello there! I’m back with a new post about implementing Johny Vino‘s BMI Calculator design. If you are not familiar with previous posts, I recommend checking them out. This time we will focus on an animation between the input screen and the result screen. We will change the shape, position and size of a Widget using only one AnimationController. Let’s see how it goes.
The goal
Since we started working on this project, the design changed a lot, therefore the transition animation also changed. However, for the educational purpose, I think it will be much better (and more fun!) if we adapt old animation to the new design. Since the animation is fairly complicated and we already have some advanced stuff here (especially in PacmanSlider), I will be skipping some of the code changes that are only relevant for this project and I will try to focus on general approaches I take to create that animation. If you are interested in the whole code, check out Github repository.
Coming back to work, this is the behavior we would like to achieve:
And this is what we have so far:
. . .
Setting up AnimationController
In order to work with animations, we need an AnimationController
, here we will use only one named _submitAnimationController
. This controller will have 2-second duration and it will get started when the user submits the PacMan slider. So far it makes an effect on the app.
. . .
Animating slider’s border
We will start our transition animation with changing slider’s borders to be more rounded. To do that, we will use BorderRadiusTween
which will help us animating from one BorderRadius to another. First, let me show the code and then explain it.
The first thing we need to do is pass AnimationController
to the PacmanSlider
widget, it can be easily done by just adding a new argument to the constructor. Then, we need to create an Animation
. As I said earlier, we can use BorderRadiusTween
for that. All we need to do is specify starting and ending radius and Flutter will take care of the rest. We also do one more thing with the Animation, we specify Interval
curve, which means that our border animation will not be run during whole 2 seconds of AnimationController
but only from start to 7% of those 2 seconds. Then we will wrap our widget in AnimatedBuilder
which will take care of rebuilding the slider everytime AnimationController
changes its value. After that we just need to use the value from_borderAnimation
and see the result:
. . .
Shrinking the slider down
Great, now let’s try to shrink that slider so that it has the shape of a circle. Let’s start by creating new Animation
that will be responsible for animating widget’s width. The difference between this animation and the border one is that we cannot initialize it in initState
method, because we want to animate slider’s width obtained from LayoutBuilder
during build
method. The animation we create will become our source of widget’s width instead of width
field.
Theoretically, it already could work, however, inside the GestureDetector
there is a lot of stuff that doesn’t go well with resizing that widget, so we will do a little hack and replace current content of slider with an empty container if the animation is animating. Thanks to that we won’t have to bother on how Pacman icon or the dots behave when sliders get too narrow. We will use animation’s isDismissed
property which tells us if an animation is stopped at the beginning.
Now we can see our slider shrinking down to the shape of a circle:
. . .
Moving the dot
We got the slider animation done, now we can work on the input page. We would like to move the dot we got from the slider to the middle of the screen. To achieve that we will create a Widget that will cover the whole Scaffold after the slider finishes its animation. We will start by wrapping the Scaffold in Stack.
Notice that we placed TransitionDot
after the Scaffold
. This way it can cover the default Scaffold. Now let’s take a look at the TransitionDot
.
Let’s break it down step by step:
- We are extending
AnimatedWidget
. It means that instead of wrapping the wholeWidget
inAnimatedBuilder
, it will be rebuilt every time the animation from constructor changes its value. - We create a new
dot
widget. It will take place of the old slider, the idea is to make it appear in the same position the slider ended up after shrinking down. - We wrap the whole widget inside
IgnorePointer
, so that it can cover the whole screen and not interact with all the controls. - We specify the widget’s opacity. If animation is below 0.15 (the point where slider animation ends), opacity is set to 0, so we cannot see the widget, otherwise, we make it visible.
- Our actual widget is a
Scaffold
, same as the one fromInputPage
but withoutAppBar
and with a different body. - So far let’s just have a
Column
with a dot as a body.
The effect looks like this:
Well, not quite impressive, right? Now we can actually animate the position of the dot. Not sure if my idea of how to do it is good, but it works so we will stick with it. So, what we want to do is surround the dot with Spacers
inside the Column
. Spacer
is an empty widget that takes as much space as possible, it also has a flex
attribute that can specify the relation of how much space of the whole container multiple spacers should take. The goal is to start with a spacer with high flex on top and very low flex in the bottom. Then, if we change the value of flexes to be equal, the dot will be between two equal spacers so it will be in the middle. To change the flex values we will use IntTween
which will increase int values from 0 to 50.
Those changes cause such result:
. . .
Animating dot’s size
Once we get the dot in the middle, we can focus on animating its size. We will create another animation that will be responsible for that, but first, we need to deal with the main problem which is that our dot is supposed to be expanded and shrunk multiple times. Usually, in scenarios like this, we add a listener to the animation and repeat it once it’s done. However, since we are having an Interval
animaiton I can’t see how it could work. So how are we going to deal with that? We are going to create our own Animatable
.
As you can see, we used sin
function to handle changing the size back and forward. Our animation will go from defaultSize
to defaultSize + expansionRange
to defaultSize - expansionRange
and then come back to deafultSize
2 times. Let’s use it ?
So we use the LoopedSizeAnimation
in a combination with Interval
. That way our dot will change its size after it got moved to the center. It should look like this:
. . .
Expanding the dot
Awesome, now we can modify that animation so that in the end it will cover the whole screen. To do that, we are going to mark a point in time, after which the dot stops pulsing and starts expanding.
What happens here is that if the time of the animation is below 80%, it will be pulsing. After that, it starts to rapidly grow from default 52 logical pixels to 2000 which should cover the whole screen. Now we need to add small changes in using the animation:
We changed two things:
- We make sure that the height and width are smaller than the screen.
- We make sure that dot changes shape to a rectangle when it gets close to the edge. Otherwise, it wouldn’t be able to cover the whole screen.
Let’s look at the result:
. . .
Fade transition
The only thing left is navigating to the new page. We will create a very simple Route
so that the transition from the blue screen to the next one will be smooth.
Now let’s use this FadeRoute
to navigate to ResultPage
. I won’t describe how this page is created since there is nothing interested there. If you are interested, you can check it out here. To navigate to the new page, we will simply add a listener to _submitAnimationController
and call Navigator
once the controller is completed.
Alright, let’s see the final result!
And that’s it! I hope you liked this post, if there is anything unclear or if you think I could explain or implement something better, please leave a comment. ?
I expect it to be the last post of the BMI Calculator since the design part is mostly done. I really enjoyed doing this app and sharing the process with you. I hoped you enjoyed it too.
If you liked the whole series, leave a star on the Github repository where you can find all the code, I would appreciate it. ?
Cheers ?
? BMI Calculator ?
Be sure to check out the other posts in the series!
Marcin Szałek
Founder of Fidev
Flutter enthusiast since Alpha release in 2017. Believes that sharing is caring, which lead him to start a technical blog dedicated fo Flutter in its early days. Loves to see beautiful designs become real apps and is willing to help make it happen. Enjoys sunny beaches far from home.
Join the newsletter!
Join the newsletter to keep track with latest posts and get my special Widget to animate views' entrances without any hassle for FREE!
Check out other posts!
Stripe Checkout in Flutter Web
Flutter Web is getting more mature every day. If you want to accept payments using Stripe Checkout in your Flutter web application, this article is just for you!
Stripe Checkout in mobile Flutter app
Have you ever struggled to integrate card payments into your mobile Flutter app? If so, today is your lucky day! In this post, I present how to use Stripe Checkout in the Flutter app without any hassle!
Interacting with Widgets using Framy
Have you ever developed a widget or a page and you wanted to make sure it works correctly in different scenarios but then it turned out that you can’t just reproduce all the cases you want to cover? Framy may solve such problems!
Thanks for the tutorial! Awesome stuff. I would love to see a full Flutter course from you. (e.g. Starting for users who have never programmed in Flutter before). You have a good way of explaining things.
Hey man! Thanks 🙂 Follow me on Twitter or Facebook so you won’t miss if such course would show up 😉
Will do! Is there anywhere you could point me to learn Flutter from the beginning? E.g. tutorials that really helped you learn, videos, books etc?
Unfortunately except Google courses on Udacity and Google’s Codelabs I cannot recommend any. Best way to learn is just to start writing an app.
Can you make post about scoped mdoel or bloc pattern?
Actually I have never used Scoped Model. Regarding BLoC, I just feel like I didn’t use it enough to share my thoughts and how I do it 🙁
But I will think about it 🙂
Hey, just spended couple f days togo through 7 parts, still i hav to dig in few math calculations if I want to implement animations in my app. Anyway Awesome articles definitely ll giv great foundation and indepth anime concepts in Flutter for future usage.
Hey, just spended couple f days togo through 7 parts, still i hav to dig in few math calculations if I want to implement animations in my app. Anyway Awesome articles definitely ll giv great foundation and indepth anime concepts in Flutter for future usage.
Hey! I’m glad you liked them! 🙂
YOU ARE GREAT MAN,
THANSK FOR EXAMPLES
YOU ARE WONDERFULL MAN
YOU ARE MY BEST
THANSK
THANK YOU
EYVALLAH
TEŞEKKÜRLER
YOU
ARE
VERY
WELCOME
MATE!