How to make ripple page transition in 3 minutes using Flutter
Flutter makes beautiful animations easy. That’s the fact. What is also the fact is that we as developers are scared to push our apps to the next level by adding those minor animations that make the app beautiful instead of just pretty. Let’s change it together! In this post, I’d like to show you how we can add a ripple effect that may make your client say “I like that!”.
Desired effect
. . .
Starting point
As a starting point, let’s have a simple app having two pages and transition between them. When a user clicks the FloatingActionButton
, he gets redirected to the second page.
And it looks like this:
Starting point
. . .
Getting FAB position
In order to perform our ripple effect, we need to have the starting position of the Floating Action Button. The easiest way to do it is to use rect_getter package by DebuggerX. Let’s add it to the pubspec.yaml
:
And now let’s use it. To do so, we need a Key
which we can later refer to in order to get FAB’s position. We will pass that key to the RectGetter
widget which will wrap out FloatingActionButton:
. . .
Adding ripple widget
If we want to have one widget on top of others, there are two Widgets we should think of: Overlay
and Stack
. Both of them let us position widgets anyway we want. Even though Overlay
would suit nicely to the expand animation, it gets a bit more tricky when we want to navigate to another page, so today we will go with a Stack
.
Having _ripple()
appear after the Scaffold
ensures, that whatever ripple will look like, it will be drawn on top of the Scaffold. How should ripple look like? For now, let’s make it an empty Container
if there is no rect
saved. If there is rect
saved, let’s draw a circle in the exact position of the Floating Action Button. To place a Widget in the Stack on the exact position, we can use Positioned
widget:
If we comment out the transition to the next page, the effect will be a blue dot covering our floating action button after it is clicked.
Before
After
. . .
Expanding ripple widget
Now it’s time to expand the widget to the full screen. There are two things we need to cover: setting the desired dimensions of the blue dot and adding animation between small and big circle.
To change the size of the ripple, we will use Rect.inflate()
method. This method causes Rect to grow in each direction by a specified distance. In our case, it will be the longest side of the screen multiplied by 1.3 just to make sure the final circle will cover the whole screen. However, we cannot change the size right after we set it to the original one (covering the fab). We need to delay it a bit, to be more specific, we just need a one frame delay. That’s why we will use WidgetsBinding.postFrameCallback
. Having that, we are sure that there is a small circle for one frame and then it becomes the big one. After a short delay, we can move to the other page. The code looks like that:
And the effect is blinking blue page transitioning to the new one:
Why is the effect so bad? Because we haven’t introduced any animations yet. The Positioned
widget simply updates its boundaries presenting the blue screen to the user. Luckily, if we want this transition from a small dot to blue screen go smoothly, we can just replace Positioned
with AnimatedPositioned
!
AnimatedPositioned will take care of our animation leaving us with the result:
And that’s it!
As you can see, we needed only Stack
, RectGetter
and AnimatedPositioned
to get this nice animation you can use in your app.
I hope that post was useful for you. The full code can be found here. If you have any questions, feel free to ask in the comment.
Cheers!
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!
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.
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!
Another great article from a greate person??.
Thanks! 🙂
You are best bro.
🙂
This is great – thank you.
Going to experiment with trying to reverse this animation too.
Sure! Let me know how it goes! 🙂
Uauuuuuuuu!!!!!
Great job man!
Thanks the best tutorial ever, simple but objective.
Hah, thanks 🙂
I’m glad you like it 🙂
Can i ask some question? How to reverse animation when i tap back button Sir?Thanks Mr, it is good article for me
Sure, what you need to do is instead of setting rect to null after the navigator.push, you have to set it to fab size, and after a delay back to null. This works:
void _goToNextPage() async {
await Navigator.of(context).push(FadeRouteBuilder(page: NewPage()));
await Future.delayed(delay);
setState(() => rect = RectGetter.getRectFromKey(rectGetterKey));
await Future.delayed(animationDuration);
setState(() => rect = null);
}
Thanks for this post. It is really helpful!
what if we want multiple buttons, directing to different route, especially how the deal with the global key for different buttons
Have different keys for each button? 😀
yea I figured that out myself, thank you anyways for the great tutorial, it put my app into another dimension