Water Drop effect in Flutter

by Mar 23, 2020Flutter3 comments

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:

  1. Get the size of the whole widget
  2. Get the center of the drop (based on WaterDropParams)
  3. Calculate the relative position of the center inside the whole widget
  4. Calculate the relative height and width inside the whole widget
  5. Subtract the relative height and width from the relative center to get the begin
  6. 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

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

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

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!

Share This

Share This

Share this post with your friends!