Phil Dukhov
Phil Dukhov

Reputation: 87615

How do I make a dotted/dashed line in Jetpack Compose?

I'd need to replicate android XML view from this answer but in Jetpack Compose with pure kotlin

Upvotes: 29

Views: 20766

Answers (4)

bmdelacruz
bmdelacruz

Reputation: 2946

To create a dotted line in compose, you need to set the line's cap to StrokeCap.Round and set the dash width (first value in the intervals array) to 0f.

Take note that setting the dash width to 0f while leaving the line's cap set to the default, which is StrokeCap.Butt, will make the line invisible.

@Preview
@Composable
private fun DottedLinePreview() {
    Canvas(
        modifier = Modifier
            .size(200.dp)
            .background(Color.White)
    ) {
        drawLine(
            color = Color.Black,
            start = Offset(10.dp.toPx(), 100.dp.toPx()),
            end = Offset(190.dp.toPx(), 100.dp.toPx()),
            strokeWidth = 5.dp.toPx(),
            cap = StrokeCap.Round, // important!
            pathEffect = PathEffect.dashPathEffect(
                intervals = floatArrayOf(
                    0f, // important!
                    8.dp.toPx(), // must be greater than stroke width
                ),
            ),
        )
    }
}

Dotted line preview

Upvotes: 1

Prem Thakur
Prem Thakur

Reputation: 192

Use the below composable wherever you want to put a dashed divider. I just made this function from Gabriele Mariotti's answer

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

@Preview
@Composable
private fun DashedDividerPreview() {
    DashedDivider(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 16.dp)
    )
}

@Composable
fun DashedDivider(
    modifier: Modifier = Modifier,
    dashWidth: Dp = 4.dp,
    dashHeight: Dp = 2.dp,
    gapWidth: Dp = 2.dp,
    color: Color = Color.Gray,
) {
    Canvas(modifier) {

        val pathEffect = PathEffect.dashPathEffect(
            intervals = floatArrayOf(dashWidth.toPx(), gapWidth.toPx()),
            phase = 0f
        )

        drawLine(
            color = color,
            start = Offset(0f, 0f),
            end = Offset(size.width, 0f),
            pathEffect = pathEffect,
            strokeWidth = dashHeight.toPx()
        )
    }
}


Upvotes: 7

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363469

You can simply use a Canvas with the method drawLine applying as pathEffect a PathEffect.dashPathEffect:

    val pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
    Canvas(Modifier.fillMaxWidth().height(1.dp)) {

        drawLine(
            color = Color.Red,
            start = Offset(0f, 0f),
            end = Offset(size.width, 0f),
            pathEffect = pathEffect
        )
    }

enter image description here

You can also apply the same pathEffect to other method as:

    val stroke = Stroke(width = 2f,
      pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
    )
    Canvas(Modifier.fillMaxWidth().height(70.dp)){
       drawRoundRect(color = Color.Red,style = stroke)
    }

enter image description here

Upvotes: 70

Phil Dukhov
Phil Dukhov

Reputation: 87615

You can create a shape in Jetpack Compose like this:

private data class DottedShape(
    val step: Dp,
) : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ) = Outline.Generic(Path().apply {
        val stepPx = with(density) { step.toPx() }
        val stepsCount = (size.width / stepPx).roundToInt()
        val actualStep = size.width / stepsCount
        val dotSize = Size(width = actualStep / 2, height = size.height)
        for (i in 0 until stepsCount) {
            addRect(
                Rect(
                    offset = Offset(x = i * actualStep, y = 0f),
                    size = dotSize
                )
            )
        }
        close()
    })
}

Usage:

Box(
    Modifier
        .height(1.dp)
        .fillMaxWidth()
        .background(Color.Gray, shape = DottedShape(step = 10.dp))
)

Result:

enter image description here

Upvotes: 36

Related Questions