Water Drop effect in Flutter
In this post, I’d like to share with you how to achieve a water drop effect in Flutter. Why would you spend your time learning such skill? Well, I have no idea but it looks cool so let’s get started! ?
TLDR: If you don’t care how it’s done and just wanna add water drops, use the water_drop package.
. . .
How it will work
The easiest to use solution would be to put an OverlayEntry
on top of everything, which would just alternate what’s beneath it. Unfortunately, to achieve this effect, we need to get a child
on which we are putting our water drops. Because of that, it will be much easier if we just use Stack
and put the drops on top of the child.
For each drop we are going to specify 4 main parameters: left
, top
, width
and height
. They are defining the position and size of the drop relative to the child
, so if we want to have a 100px by 100px drop in the top left corner, we pass left and top as zeros, and height and width as 100s.
We also want to provide a Widget which will allow developers to easily add multiple drops (like in the video at the beginning of this article), so we will use a Stack
in which we will add _WaterDrops
for each WaterDropParam
:
. . .
Cutting out the drop
At first, we need to take only the part of the widget we are going to use. To do that, we can use ClipPath
widget with our own CustomClipper
. It allows us to… well… cut out the portion of the widget. ? We will start with creating very simple OvalClipper
which will take care of defining what to cut out and what to leave:
To use it, we need to pass the OvalClipper
in ClipPath
widget just like in the code below. We also need to specify clipBehavior
to Clip.hardEdge
– this will prevent us from getting undesired artifacts later on. We put everything inside the Stack
as we will add more elements below and above the ClipPath
soon and we wrap everything in an IgnorePointer
so that the water drop doesn’t interfere with user’s gestures.
Right now we are not altering the cut out drop at all, so it’s not even noticeable that we do something, but if we remove the original child from the Stack
for a moment, we can see how our cut out looks like:
. . .
Gradient overlay
Why do we actually think that something is a water drop? Mostly because the light looks different there. So we need to add some minor changes which will imitate that in this place, something special is taking place. We will start by putting a LinearGradient
on top of our drop.
We’d like to have the gradient start in the top left corner with black color and smoothly change to white in the bottom right corner. At start, it seems rather easy, we just use BoxDecoration
with LinearGradient
, set the begin to Alignment.topLeft
and the end to Alignment.bottomRight
and we’re good! Well, it’s not that simple. ? If we wrap our child (even cut out) with the gradient, the topLeft
will be pointing to the top left corner of the whole widget (in our case a Card
), not the drop itself! If we want to have the gradient just over the drop, we need to calculate the alignment by ourselves.
How do we do it then? Well, we just need to implement the following steps:
- Get the size of the whole widget
- Get the center of the drop (based on
WaterDropParams
) - Calculate the relative position of the center inside the whole widget
- Calculate the relative height and width inside the whole widget
- Subtract the relative height and width from the relative center to get the begin
- Add he relative height and width from the relative center to get the end
It may look like there’s a lot of work but the following code does it all:
Once we have the alignments, all we need to do is wrap our child
in Container
with BoxDecoration
. What’s important here is that we need to set backgroundBlendMode
to BlendMode.overlay
. This way our gradient will not just cover the water drop but it will “merge” with it and be much more subtle.
. . .
Light and shadow
Now let’s add a minor light reflection above the drop and shadow beneath it. Since we know the left
, top
, height
and width
of the drop, we can just use Positioned
to specify the position and size of both the shadow and the light. Putting them inside the Stack
will also allow us to have the shadow under the drop and the light on top of it.
Now we only need to put them inside the Stack
:
And now it will look like this:
. . .
Distortion effect
Now it’s time to create a distortion effect where the view inside a bubble is a bit bigger and where it kinda bends on the edges of the drop. Surprisingly, it is very simple to achieve such a result.
What we can do, is put a few layers on top of each other. Each layer of the drop will be a bit smaller (the radius of the cutout will be smaller) but the content inside will be a bigger (we will scale what’s inside). If we have a several layers like that, it will create a zooming effect. If those layers will be different by a small factor, the whole effect will look very smooth. The code for this is very simple. All we have to do is generate around 8 ClipPaths
, each having smaller width
and height
. Then we wrap each of them in Transform.scale
to create zooming effect:
What’s worth noting here is that we are having 8 extra builds for each drop. It can cause performance problems so please think twice before using it! Especially if you want to wrap the whole page in it!
. . .
And that’s it!
Now we can add more WaterDrops
and even bind the parameters with the AnimationController
to make them move:
As always, the whole code is on the Github: https://github.com/MarcinusX/water_drop
You can also just use it as a package: https://pub.dev/packages/water_drop
If you have any questions or ideas for improvements, let me know in the comments!
Cheers! ?
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!
Amazing effect sir, this is the first time I saw a unique effect implemented in a flutter application. Expecting more such great works from you???
Thank you! I’m glad you liked it! 🙂
Awesome!
Maybe you can explain how to make 3d ripple water effect too?
App with effect, time:1:20
https://youtu.be/cuzTw_DTM2w?t=78