
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:

It is not giving me a propers shape. Corner shape are not properly shaped.
Please help to achieve this shape in jetpack compose.
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,
)
}

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
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
)
}
})

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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With