Posted in

How to make Bezier Curve waves using custom clip Path in Flutter

How to make Bezier Curve waves using custom clip Path in Flutter
How to make Bezier Curve waves using custom clip Path in Flutter

Ever seen those beautiful, smooth, and organic-looking waves in app UIs and wondered how they’re made? Often, the answer is Bezier curves. They are a fantastic way to move beyond simple straight lines and boxes to create fluid, dynamic shapes that make your app’s design stand out.

In Flutter, the key to creating these custom shapes is the ClipPath widget combined with a custom clipper. This guide will walk you through exactly how to create your own beautiful wave effects from scratch.

The Core Concept: ClipPath and CustomClipper

To clip a widget into a custom shape, Flutter gives us the ClipPath widget. It takes two main properties:

  1. child: The widget you want to clip (e.g., a Container with a color or gradient).
  2. clipper: An object that defines the shape of the clip. This is where the magic happens.

The clipper must be an instance of a class that extends CustomClipper<Path>. This custom class is where we will define the actual path of our wave.

Step 1: Create a Custom WaveClipper Class

First, let’s create a new class, which we’ll call WaveClipper, that will contain the logic for our wave shape.

A CustomClipper class requires you to implement two methods:

  • getClip(Size size): This method is where you define the shape. It returns a Path. The size parameter gives you the height and width of the child widget you are clipping, allowing you to make your shape responsive.
  • shouldReclip(CustomClipper<Path> oldClipper): This method is called whenever the ClipPath‘s properties change. It’s an optimization. For a static wave like ours, we can simply return false because the path doesn’t need to be recalculated unless the clipper itself changes.

Here is the basic structure:

import 'package:flutter/material.dart';

class WaveClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    // This is where we will build our wave path
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false; // We don't need to reclip for this static shape
  }
}
Dart

Step 2: Drawing the Bezier Curve Wave

Now for the fun part! Inside the getClip method, we’ll use a Path object to draw our shape. A quadratic Bezier curve is perfect for a simple wave. It’s defined by a start point, an end point, and a single “control point” that pulls the line towards it to create the curve.

Here’s the logic for drawing a single wave:

@override
Path getClip(Size size) {
  final path = Path();
  
  // 1. Start from the top-left corner
  path.lineTo(0, size.height * 0.8); // Go down 80% of the height

  // 2. Draw the Bezier curve
  // The control point is in the middle of the width and a bit lower than the start
  var firstControlPoint = Offset(size.width / 4, size.height);
  var firstEndPoint = Offset(size.width / 2, size.height * 0.85);
  path.quadraticBezierTo(
    firstControlPoint.dx,
    firstControlPoint.dy,
    firstEndPoint.dx,
    firstEndPoint.dy,
  );

  // 3. Draw a second Bezier curve for a more complex wave
  var secondControlPoint = Offset(size.width * 0.75, size.height * 0.7);
  var secondEndPoint = Offset(size.width, size.height * 0.8);
  path.quadraticBezierTo(
    secondControlPoint.dx,
    secondControlPoint.dy,
    secondEndPoint.dx,
    secondEndPoint.dy,
  );

  // 4. Go to the top-right corner
  path.lineTo(size.width, 0);
  
  // 5. Close the path
  path.close();

  return path;
}
Dart

This code creates a path that forms a shape with a double-humped wave along the bottom.

Step 3: Putting It All Together

Now, let’s use our WaveClipper with a ClipPath widget to clip a Container. A Stack is perfect for placing the wave at the top of the screen behind other content.

Full Code Example

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: WaveScreen(),
    );
  }
}

class WaveScreen extends StatelessWidget {
  const WaveScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // The main content of the screen
          const Center(
            child: Text(
              'Your Content Here',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
          ),
          
          // The wave at the top
          ClipPath(
            clipper: WaveClipper(),
            child: Container(
              height: 220, // The height of the wave
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  colors: [Colors.blue, Colors.lightBlueAccent],
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// --- Place the WaveClipper class from Step 2 here ---
class WaveClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    final path = Path();
    path.lineTo(0, size.height * 0.8);

    var firstControlPoint = Offset(size.width / 4, size.height);
    var firstEndPoint = Offset(size.width / 2, size.height * 0.85);
    path.quadraticBezierTo(
      firstControlPoint.dx,
      firstControlPoint.dy,
      firstEndPoint.dx,
      firstEndPoint.dy,
    );

    var secondControlPoint = Offset(size.width * 0.75, size.height * 0.7);
    var secondEndPoint = Offset(size.width, size.height * 0.8);
    path.quadraticBezierTo(
      secondControlPoint.dx,
      secondControlPoint.dy,
      secondEndPoint.dx,
      secondEndPoint.dy,
    );

    path.lineTo(size.width, 0);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false;
  }
}
Dart

Conclusion

And there you have it! By defining a custom path with Bezier curves, you can clip any widget into a beautiful, fluid shape. This technique opens up a world of creative possibilities for your app’s UI.

Don’t be afraid to experiment! Adjust the x and y coordinates of the control points and end points in your WaveClipper to create all kinds of unique and stunning wave effects. Happy fluttering!

Flutter Stuff Is A Team Of Passionate Flutter Developers On A Mission To Empower The Community. We Share Our Expertise And Insights Through Comprehensive Guides, Tutorials, And Resources, Making Flutter Mobile App Development Accessible And Enjoyable For Everyone.

Leave a Reply