How to have dashed border in Jetpack Compose?
Asked Answered
S

4

44

I can easily create a normal border using the Modifier.border() but how to create a dashed border as shown in the image below.

enter image description here

Schuler answered 1/3, 2021 at 18:27 Comment(0)
S
17

After some digging in the normal border modifier, I found out that it uses Stroke object which can take a parameter PathEffect that can make it dashed, here is a modified version of the normal border function that takes this parameter.

https://gist.github.com/DavidIbrahim/236dadbccd99c4fd328e53587df35a21

Schuler answered 2/3, 2021 at 13:38 Comment(1)
After the question above I started digging into this and was stuck at this error: "Type mismatch: inferred type is DashPathEffect but PathEffect was expected." This worked before alpha-09 but not on beta. After your answer I got it working. Thank you!Trover
R
67

There isn't a parameter in Modifier.border() to achieve a dashed path.

However you can use a DrawScope to draw a dashed Path using PathEffect.dashPathEffect.

Something like:

val stroke = Stroke(width = 2f,
    pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
)

You can draw it using the drawBehind modifier:

Box(
    Modifier
        .size(250.dp,60.dp)
        .drawBehind {
            drawRoundRect(color = Color.Red, style = stroke)
        },
    contentAlignment = Alignment.Center
) {
    Text(textAlign = TextAlign.Center,text = "Tap here to introduce yourseft")
}

enter image description here

If you want rounded corner just use the cornerRadius attribute in the drawRoundRect method:

drawRoundRect(color = Color.Red,style = stroke, cornerRadius = CornerRadius(8.dp.toPx()))

enter image description here


If you prefer you can build your custom Modifier with the same code above. Something like:

fun Modifier.dashedBorder(strokeWidth: Dp, color: Color, cornerRadiusDp: Dp) = composed(
    factory = {
        val density = LocalDensity.current
        val strokeWidthPx = density.run { strokeWidth.toPx() }
        val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }

        this.then(
            Modifier.drawWithCache {
                onDrawBehind {
                    val stroke = Stroke(
                        width = strokeWidthPx,
                        pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
                    )

                    drawRoundRect(
                        color = color,
                        style = stroke,
                        cornerRadius = CornerRadius(cornerRadiusPx)
                    )
                }
            }
        )
    }
)

and then just apply it:

Box(
    Modifier
        .size(250.dp,60.dp)
        .dashedBorder(1.dp, Red, 8.dp),
    contentAlignment = Alignment.Center
) {
    Text(
        text = "Tap here to introduce yourself",
        textAlign = TextAlign.Center,
    )
}
Reniti answered 10/4, 2021 at 22:4 Comment(5)
I don't understand why there is no longer Modifier.border()? Actually I am using android beta 04 and it's not deprecatedSchuler
@DavidIbrahim The Modifier.border() exists and it is not deprecated.Reniti
Ohh sorry I misunderstood, I thought you are providing a different solution for this problem because of my code going deprecated lol.Schuler
@GabrieleMariotti if I need add radius what I need to do?Padus
@CôngHải check the updated answer. Just use the cornerRadius attribute in the drawRoundRect method.Reniti
S
17

After some digging in the normal border modifier, I found out that it uses Stroke object which can take a parameter PathEffect that can make it dashed, here is a modified version of the normal border function that takes this parameter.

https://gist.github.com/DavidIbrahim/236dadbccd99c4fd328e53587df35a21

Schuler answered 2/3, 2021 at 13:38 Comment(1)
After the question above I started digging into this and was stuck at this error: "Type mismatch: inferred type is DashPathEffect but PathEffect was expected." This worked before alpha-09 but not on beta. After your answer I got it working. Thank you!Trover
L
15

I wrote this extension for the Modifier you can simply use it or modify it.

fun Modifier.dashedBorder(width: Dp, radius: Dp, color: Color) = 
    drawBehind {
        drawIntoCanvas {
            val paint = Paint()
                .apply {
                    strokeWidth = width.toPx()
                    this.color = color
                    style = PaintingStyle.Stroke
                    pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
        }
        it.drawRoundRect(
            width.toPx(),
            width.toPx(),
            size.width - width.toPx(),
            size.height - width.toPx(),
            radius.toPx(),
            radius.toPx(),
            paint
        )
    }
}
Lorilee answered 29/1, 2022 at 15:50 Comment(1)
Using this as is my border had a bit of a gap from the actual edge of the view. Updating to this fixed that it.drawRoundRect( (width.toPx() / 2), (width.toPx() / 2), size.width - (width.toPx() / 2), size.height - (width.toPx() / 2), radius.toPx(), radius.toPx(), paint )Disconsolate
A
3
@Composable
private fun AddNewFileButton(
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
) {
    val stroke = Stroke(
        width = 4f,
        pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
    )

    Box(
        modifier = modifier
            .drawBehind {
                drawRoundRect(
                    color = Color.Blue,
                    style = stroke,
                    cornerRadius = CornerRadius(16.dp.toPx())
                )
            }
            .clip(RoundedCornerShape(16.dp))
            .clickable { onClick() },
    ) {
        Row(
            modifier = modifier.padding(16.dp),
            horizontalArrangement = Arrangement.Center
        ) {
            Text(
                text = "Add new item"
            )
        }
    }
}

enter image description here

Allfired answered 22/1 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.