Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw or create this curve shape in Jetpack Compose?

enter image description here

I tried using Path and bezier curves concept using cubicTo() method.

I need height 70dp and width 333dp.

 @Composable
fun CurveShape() {
    val path = remember { Path() }

    Canvas(
        modifier = Modifier
            .height(80.dp)
            .fillMaxWidth()
            .background(Color.White)

            //.size(666.dp, 80.dp)
    ) {
        path.reset()
       
        path.cubicTo(x1 = 400f, y1 = 150f, x2 = 500f, y2 = 400f, x3 = size.width, y3 = 00.0f)
   
        drawPath(
            color = Color.Black,
            path = path,
        )
    }
}

This is giving me the output like below:

enter image description here

It is not giving me a propers shape. Corner shape are not properly shaped.

Please help to achieve this shape in jetpack compose.

like image 681
Yogesh Nikam Patil Avatar asked Feb 02 '26 21:02

Yogesh Nikam Patil


2 Answers

Solution still using Canvas(..)

You will need to extend the Path out with a secondary path.cubicTo(..) call.

Here's a quick and dirty function I've written to generate these cubic curves based on bottomLeft and TopRight values of the curve in the canvas

@Composable
fun CurveShape() {
    Canvas(
        modifier = Modifier
            .height(80.dp)
            .fillMaxWidth()
            .background(Color.White)
    ) {
        drawCubicCurve(
            bottomLeft = Offset(0f, size.height),
            topRight = Offset(size.width, 0f),
            color = Color.Black
        )
    }
}

fun DrawScope.drawCubicCurve(
    bottomLeft: Offset,
    topRight: Offset,
    color: Color = Color.White
) {
    val x1Modifier = 0.41f
    val x2Modifier = 0.22f
    val x3Modifier = 0.5f
    val y1Modifier = 0f
    val y2Modifier = 1f
    val y3Modifier = 1f

    val delta = topRight.minus(bottomLeft)
    val width = delta.x
    val height = delta.y

    val path = Path()
    path.moveTo(bottomLeft.x, bottomLeft.y)
    path.cubicTo(
        x1 = bottomLeft.x.plus(width.times(x1Modifier)),
        y1 = bottomLeft.y.plus(height.times(y1Modifier)),
        x2 = bottomLeft.x.plus(width.times(x2Modifier)),
        y2 = topRight.y.times(y2Modifier),
        x3 = bottomLeft.x.plus(width.times(x3Modifier)),
        y3 = topRight.y.times(y3Modifier),
    )
    path.cubicTo(
        x1 = topRight.x.minus(width.times(x2Modifier)),
        y1 = topRight.y.times(y2Modifier),
        x2 = topRight.x.minus(width.times(x1Modifier)),
        y2 = bottomLeft.y.plus(height.times(y1Modifier)),
        x3 = topRight.x,
        y3 = bottomLeft.y,
    )
    drawPath(
        path = path,
        alpha = 0.5f,
        brush = Brush.verticalGradient(
            colors = listOf(
                color.copy(alpha = 0.0f),
                color.copy(alpha = 0.7f),
                color
            ), startY = bottomLeft.y, endY = topRight.y
        )
    )
    drawPath(
        color = color,
        path = path,
    )
}

Curve

My code draws the curve the other way around as you want it, so I encourage you to play around with the values here to get the resulting curve you want :)

Additionally calling .reset() is only required because you have remembered the Path(..). I'd advise you to not remember the Path() as you want to recalculate it whenever a recomposition occurs as the measurements of the Canvas may have changed. Resulting in a different path

like image 172
ElliotSknr Avatar answered Feb 05 '26 12:02

ElliotSknr


I think using two bazier curves would give you the dezired result:

Spacer(modifier = Modifier
            .height(80.dp)
            .width(333.dp)
            .background(Color.White)
            .drawWithCache {
                onDrawBehind {
                    val path = Path()
                    path.moveTo(0f, 0f)
                    path.cubicTo(x1 = 333.dp.toPx()/10, y1 = 0f, x2 = 333.dp.toPx()/4, y2 = 70.dp.toPx(), x3 = 333.dp.toPx()/2, y3 = 70.dp.toPx())
                    path.cubicTo(x1 = 333.dp.toPx()*3/4, y1 = 70.dp.toPx(), x2 = 333.dp.toPx()-333.dp.toPx()/10, y2 = 0f, x3 = 333.dp.toPx(), y3 = 0f)
                    drawPath(
                        path = path,
                        color = Color.Black,
                        style = Fill
                    )
                }
            })

correspoding image

With moveTo() function we assign the starting point of the bezier curve. The destination points are determined by (x3,y3) coordinates.
(x1,y1) act as anchor points for the start of the curve and (x2,y2) act as anchor points for destination of the curve.

For more details on how these anchor points affect the curve checkout this answer.

like image 25
Mr. Techie Avatar answered Feb 05 '26 12:02

Mr. Techie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!