flutterdartflutter-custompainter

How to match this curved profile banner shape?


I’m trying to replicate a custom curved profile banner shape using CustomPaint in Flutter.

This is the desired shape I’m trying to achieve — notice how the bottom of the banner has a smooth inward curve (concave) with nice rounded edges:

enter image description here

However, here’s the shape I’m currently getting — the curve doesn’t match, and the corners are not blending correctly:

enter image description here

and this is my current code

My Current Code

What I need help with: How to match the exact smooth bottom curve of the profile banner as shown in the first image.

Ensuring the edges curve correctly and symmetrically from left to right.

I’m open to using cubicTo, quadraticBezierTo, or even arcToPoint — whichever helps match the reference shape.

Would really appreciate any pointers on fixing the Path logic.


Solution

  • I used CustomClipper and achieved the same output as shown in your image. Try the code below.

    @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Container(
              color: Colors.black,
              height: 300,
              width: 400,
              child: Stack(
                alignment: Alignment.topCenter,
                children: [
                  ClipPath(
                    clipper: WaveClipper(),
                    child: Container(width: 400, height: 300, color: Colors.red),
                  ),
    
                  Positioned(
                    bottom: 15,
                    child: Container(
                      height: 130,
                      width: 130,
                      decoration: BoxDecoration(
                        color: Colors.green,
                        shape: BoxShape.circle,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    
    class WaveClipper extends CustomClipper<Path> {
      @override
      Path getClip(Size size) {
        double waveHeight = 100;
        double waveWidth = 65;
        double blackHeight = 90;
        double cornerCurve = 40;
    
        final path = Path();
        path.moveTo(0, 0);
        path.lineTo(0, size.height - blackHeight);
    
        path.quadraticBezierTo(
          (size.width / 2) - waveWidth - cornerCurve,
          size.height - blackHeight,
          (size.width / 2) - waveWidth,
          size.height - blackHeight - cornerCurve,
        );
    
        path.quadraticBezierTo(
          size.width / 2,
          size.height - blackHeight - waveHeight,
          (size.width / 2) + waveWidth,
          size.height - blackHeight - cornerCurve,
        );
    
        path.quadraticBezierTo(
          (size.width / 2) + waveWidth + cornerCurve,
          size.height - blackHeight,
          size.width,
          size.height - blackHeight,
        );
        path.lineTo(size.width, 0);
        path.close();
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => false;
    }