Flutter

Clip bezier curve animation in Flutter

By April 22, 2020 February 9th, 2021 No Comments

The context and goal

Short story. I got a client who wanted to make a cool App. They provided a design they wanted. Here it is one of the screens:

The original design was a simple curve at the bottom of a section.

I could stop there, just make the simple curve, but I think the details make the difference.

One of the things I love about Flutter is how easy it is to add animations.

So just in some more lines of code, I added a simple animation with this result:

 

Isn’t it cool? Making the bouncing curve, users will find it funnier rather than having a simple static curve.

 

How to do it.

Step1. Create a static bezier curve section.

In order to achieve this result it is clear we need to use a Container with a LinearGradient as a background.

 Container(decoration: BoxDecoration(gradient: LinearGradient(
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
      colors: [MyOrange, MyPink]));

//colors
Color MyOrange = const Color(0xFFFF881B);
Color MyPink = const Color(0xFFE62E6F);

Great we have a rectangle with the gradient background.

Let’s use ClipPath to wrap this Container and create the curves.

ClipPath(
      child: _getContainerWithGradient(),
      clipper: BottomWaveClipper(),
    )

BottomWaveClipper is a CustomClipper where we define the shape of the clip. In other words, we will create the curved Path.

If we extend BottomWaveClipper with CustomClipper we have the following, since we must implement two methods.

class BottomWaveClipper extends CustomClipper<Path>{
  @override
  Path getClip(Size size) {
    // TODO: implement getClip
    throw UnimplementedError();
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) =>false //return false for the moment
}

Let’s focus on the getClip methods. So, how we build this Path to create the curved effect? Clearly we need to use the bezier curves.

The image above shows the original design with the points of the curve to use it as a reference.

First create a Path and move the pointer to the bottom left side of the rectangle, where the curve will start.

@override
Path getClip(Size size) {
  var path = Path();
  path.lineTo(0.0, size.height);   //move point to bottom left of the container

Fortunately Path class provides a simple way to create a bezier curve.

/// Adds a quadratic bezier segment that curves from the current
/// point to the given point (x2,y2), using the control point
/// (x1,y1).
void quadraticBezierTo(double x1, double y1, double x2, double y2) native 'Path_quadraticBezierTo';

So from the current point (0.0, size.height) to (x2,y2) using x1,y1 as control point.

We can see in the design the x2,y2, is the filled blue point in the middle of the curve (point #4), and the control point the higher point between the first white point and the blue point (point #3)

path.quadraticBezierTo(size.width / 6, size.height-40, //point #3
        size.width / 2, size.height-20); //point #4

To simplify, I just moved the end point to exactly the middle of the Container. In the original design, the curve has the point slightly moved to the left.

This is the design modified with the simplified measures I will use:

Let’s see the result!

Next curve

var secondControlPoint =
      Offset(size.width - size.width/6, size.height); //#point #5
  var secondEndPoint = Offset(size.width, size.height-30); //point #6
  path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
      secondEndPoint.dx, secondEndPoint.dy);

 

Now we just need to go back to the origin to close or path properly.

path.lineTo(size.width, size.height); //move to top right #7
  path.lineTo(size.width, 0.0); //and back to the origin, could not be necessary #1
  path.close();

 

Step 2. Create the animation

The final step, create the animation. You already imagine how easy can be to implement it, right?

We just could wrap this in a StatefulWidget with the Animation. In Android Studio just set the cursor on the word StatelessWidget, and press Cmd+Enter (⌘+Enter). Should appear the option to convert to StatefulWidget.

Or just use the shortcut “stanim” to create a StatefulWidget with an animation template.

But to make it easier we can use TweenAnimationBuilder, this simplifies the animation we want to achieve.

If you are not familiar with animation in Flutter I recommend this series of official videos

TweenAnimationBuilder requires 4 parameters:

  • child: widget, the Container with the LinearGradient.
  • curve, for our animation we’ll use the  Curves.elasticOut,
  • tween: We want to interpolate from -50 to 0, so this value will be subtracted or added in the control points of our bezier curve.
  • duration:  Duration of the animation , we’ll put just 2 seconds.
  • builder: Function to create the widget, where we’ll use the animation value.

So now, instead of having a CustomClipper Widget. we will have a TweenAnimation Builder like this:

return TweenAnimationBuilder(
     child: widget, //any content inside the container
     curve:   Curves.elasticOut,
     tween: Tween(begin: -50.0,end:0.0) ,
     duration: Duration(seconds: 2),
     builder: (ctx,value,child){
     return ClipPath(
       child: child,
       clipper: BottomWaveClipper(value:value),
     );
 },);

Note that now BottomWaveClipper takes as a parameter the value of the animation.

Now we just need to use this value when creating the bezier curve. In my case, I just use it in control points.

path.quadraticBezierTo(size.width / 6, size.height-40 - value, size.width / 2, size.height-20);

and

var secondControlPoint =
      Offset(size.width - size.width/6, size.height+value);

 

We still need to implement the shouldReclip method. Pretty  easy, we need to compare the value of the animation and see if it is the same.

@override
 bool shouldReclip(CustomClipper<Path> oldClipper) =>
     oldClipper is BottomWaveClipper && value != oldClipper.value;

 

Here we go, nice animation in record time!

Here it is a running example Dartpad.

But go on and experiment applying the value of the animation to the different parts of the curve. And that’s it!

Conclusion

Easy, fast, and colorful.

Just combining some Widgets we have created a cool effect. We have added some more value (a little detail) to our App. The users, the clients will be happy to see these details while using your App!  💙

Ernie

Author Ernie

More posts by Ernie