Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shapes in Jetpack compose android

I'm trying to create a custom card in Jetpack Compose that has a specific shape. I want the card to resemble a particular shape (as shown in the image below) with rounded corners and a custom contour.

I've started with the code below, but I'm unsure how to adjust the curves and the shape to match the image exactly. Here’s the target card shape image:

Custom Card

How can I make a background similar to the picture?

Here is my code, I have tried but couldn't achieve similar shape.

@Composable
fun CustomCard(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val radius = with(LocalDensity.current) { 19.dp.toPx() }
    val cornerRadiusDp = 80.dp
    val cornerRadius = with(LocalDensity.current) { cornerRadiusDp.toPx() }

    val padding = 10.dp

    // Create a custom shape
    val shape = remember {
        GenericShape { size, _ ->
            val width = size.width
            val height = size.height

            moveTo(0f, radius)
            quadraticTo(0f, 0f, radius, 0f)
            lineTo(width - cornerRadius - radius, 0f)
            quadraticTo(width - cornerRadius, 0f, width - cornerRadius, radius)

            quadraticTo(width - 1.1f*cornerRadius, 1.1f*cornerRadius, width - radius, cornerRadius)

            quadraticTo(width, cornerRadius, width, cornerRadius + radius)

            lineTo(width, height - radius)
            quadraticTo(width, height, width - radius, height)

            lineTo(radius, height)
            quadraticTo(0f, height, 0f, height - radius)

            lineTo(0f, radius)
        }
    }

    Box(modifier = modifier) {
        Card(
            modifier = Modifier.fillMaxSize(),
            shape = shape,
            colors = CardDefaults.cardColors(
                containerColor = Color(0xFFBFE8FB)
            )
        ) {
            content()
        }

        Box(
            modifier = Modifier
                .size(cornerRadiusDp - padding)
                .align(Alignment.TopEnd)
                .background(
                    color = Color.Blue,
                    shape = CircleShape
                )
        ){
            Text(
                text = "1",
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
    }
}

@Preview
@Composable
fun ContextCardPreview() {
    Column(modifier = Modifier.background(Color.Red)){
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            CustomCard(modifier = Modifier
                .width(200.dp)
                .height(180.dp)
            ) {

                val price = buildAnnotatedString {
                    withStyle(SpanStyle(color = Color.Green)) { append("$") }
                    append("0.00")
                }

                val labelStyle = remember {
                    TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Light)
                }

                val priceStyle = remember {
                    TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Bold)
                }

                Column(
                    modifier = Modifier.padding(10.dp)
                ) {
                    Text(text = "Text", fontSize = 18.sp)
                    //Text(text = price, style = priceStyle)

                    Spacer(modifier = Modifier.weight(0.8f))

                    Text(text = "Text", style = labelStyle)

                    Spacer(modifier = Modifier.weight(0.5f))
                    Text(text = price, style = priceStyle)
                    Spacer(modifier = Modifier.weight(0.04f))
                }
            }
        }

    }
}
like image 909
rlaj Avatar asked Aug 06 '26 18:08

rlaj


1 Answers

This is an improved version of this answer, which gives you even more control over the outline of the shape.

/**
 * cornerRadius : control the corner radius
 * gapCornerRadius : radius of the gap corner
 * gapSize : size of the gap
 * padding : padding between gap and button circle
 * concavityFactor : control the concavity of the gap
 */
@Composable
fun CustomCard(
    modifier: Modifier = Modifier,
    cornerRadius : Dp = 20.dp,
    gapCornerRadius : Dp = 30.dp,
    gapSize : Dp = 80.dp,
    padding: Dp = 10.dp,
    concavityFactor: Float = 1.05f,
    content: @Composable () -> Unit,
) {
    val cornerRadiusPx = with(LocalDensity.current) { cornerRadius.toPx() }

    val gapCornerRadiusPx = with(LocalDensity.current) { gapCornerRadius.toPx() }

    val gapSizePx = with(LocalDensity.current) { gapSize.toPx() }

    // Create a custom shape
    val shape = remember {
        GenericShape { size, _ ->
            val width = size.width
            val height = size.height

            moveTo(0f, cornerRadiusPx)
            quadraticTo(0f, 0f, cornerRadiusPx, 0f)

            lineTo(width - gapSizePx - gapCornerRadiusPx, 0f)
            quadraticTo(width - gapSizePx, 0f, width - gapSizePx, gapCornerRadiusPx)

            quadraticTo(width - concavityFactor*gapSizePx, concavityFactor*gapSizePx, width - gapCornerRadiusPx, gapSizePx)

            quadraticTo(width, gapSizePx, width, gapSizePx + gapCornerRadiusPx)

            lineTo(width, height - cornerRadiusPx)
            quadraticTo(width, height, width - cornerRadiusPx, height)

            lineTo(cornerRadiusPx, height)
            quadraticTo(0f, height, 0f, height - cornerRadiusPx)

            lineTo(0f, cornerRadiusPx)
        }
    }

    Box(modifier = modifier) {
        Card(
            modifier = Modifier.fillMaxSize(),
            shape = shape,
            colors = CardDefaults.cardColors(
                containerColor = Color(0xFFBFE8FB)
            )
        ) {
            content()
        }

        Box(
            modifier = Modifier
                .size(gapSize - padding)
                .align(Alignment.TopEnd)
                .background(
                    color = Color.Blue,
                    shape = CircleShape
                )
        ){
            Text(
                text = "1",
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
    }
}

You can play with the CustomCard parameters to see the effect of each argument. This way you can use whatever values ​​you like.

Here is an image to see each parameter visually:

enter image description here

like image 81
Abdo21 Avatar answered Aug 08 '26 15:08

Abdo21



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!