1

There are items() {} sections inside LazyColumn. So I would like to draw a border with rounded corners around each section. Is there any method?

// need to draw a border around the items
LazyColumn {
    items(10) {
        Row {
            // content
        }
    }

    items(5) {
        Row {
            // content
        }
    }
}
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841

3 Answers3

5

If you want to add a border to single item just add in your item content a Composable with a border modifier:

items(10) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(2.dp)
                .border(width = 1.dp, color = Blue200, shape = RoundedCornerShape(8.dp))
                .padding(2.dp)
       ){ /** ... */ }
}

enter image description here

If you want to add a border around all the items block you can create different border modifiers to apply to each items.
Something like:

//border
val strokeWidth: Dp = 2.dp
val strokeColor: Color = Blue500
val cornerRadius: Dp = 8.dp

//background shape
val topShape = RoundedCornerShape(topStart = cornerRadius, topEnd = cornerRadius)
val bottomShape = RoundedCornerShape(bottomStart = cornerRadius, bottomEnd = cornerRadius)

LazyColumn {
    val itemCount = 10
    var shape : Shape
    var borderModifier : Modifier

    items(itemCount) { index ->
        when (index) {
            0 -> {
                //First item. Only top border
                shape = topShape
                borderModifier = Modifier.topBorder(strokeWidth,strokeColor,cornerRadius)
            }
            itemCount -1 -> {
                //last item. Only bottom border
                shape = bottomShape
                borderModifier = Modifier.bottomBorder(strokeWidth,strokeColor,cornerRadius)
            }
            else -> {
                //Other items. Only side border
                shape = RectangleShape
                borderModifier = Modifier.sideBorder(strokeWidth,strokeColor,cornerRadius)
            }
        }

        Row(
            modifier = Modifier
                .fillMaxWidth()
                .clip(shape)
                .background(Teal200)
                .then(borderModifier)
                .padding(4.dp)
        ) {
            Text(text = "Item: $index")
        }
    }
}

enter image description here

where:

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

        Modifier.drawBehind {
            val width = size.width
            val height = size.height

            drawLine(
                color = color,
                start = Offset(x = 0f, y = height),
                end = Offset(x = 0f, y = cornerRadiusPx),
                strokeWidth = strokeWidthPx
            )

            drawArc(
                color = color,
                startAngle = 180f,
                sweepAngle = 90f,
                useCenter = false,
                topLeft = Offset.Zero,
                size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                style = Stroke(width = strokeWidthPx)
            )

            drawLine(
                color = color,
                start = Offset(x = cornerRadiusPx, y = 0f),
                end = Offset(x = width - cornerRadiusPx, y = 0f),
                strokeWidth = strokeWidthPx
            )

            drawArc(
                color = color,
                startAngle = 270f,
                sweepAngle = 90f,
                useCenter = false,
                topLeft = Offset(x = width - cornerRadiusPx * 2, y = 0f),
                size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                style = Stroke(width = strokeWidthPx)
            )

            drawLine(
                color = color,
                start = Offset(x = width, y = height),
                end = Offset(x = width, y = cornerRadiusPx),
                strokeWidth = strokeWidthPx
            )
        }
    }
)

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

        Modifier.drawBehind {
            val width = size.width
            val height = size.height

            drawLine(
                color = color,
                start = Offset(x = 0f, y = 0f),
                end = Offset(x = 0f, y = height-cornerRadiusPx),
                strokeWidth = strokeWidthPx
            )

            drawArc(
                color = color,
                startAngle = 90f,
                sweepAngle = 90f,
                useCenter = false,
                topLeft = Offset(x = 0f, y = height - cornerRadiusPx * 2),
                size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                style = Stroke(width = strokeWidthPx)
            )

            drawLine(
                color = color,
                start = Offset(x = cornerRadiusPx, y = height),
                end = Offset(x = width - cornerRadiusPx, y = height),
                strokeWidth = strokeWidthPx
            )

            drawArc(
                color = color,
                startAngle = 0f,
                sweepAngle = 90f,
                useCenter = false,
                topLeft = Offset(x = width - cornerRadiusPx * 2, y = height - cornerRadiusPx * 2),
                size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                style = Stroke(width = strokeWidthPx)
            )

            drawLine(
                color = color,
                start = Offset(x = width, y = 0f),
                end = Offset(x = width, y = height - cornerRadiusPx),
                strokeWidth = strokeWidthPx
            )
        }
    }
)

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

        Modifier.drawBehind {
            val width = size.width
            val height = size.height

            drawLine(
                color = color,
                start = Offset(x = 0f, y = 0f),
                end = Offset(x = 0f, y = height),
                strokeWidth = strokeWidthPx
            )

            drawLine(
                color = color,
                start = Offset(x = width, y = 0f),
                end = Offset(x = width, y = height),
                strokeWidth = strokeWidthPx
            )
        }
    }
)
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 1
    Thanks! The second solution works for me. One thing to note I made offsets from the border sides to `strokeWidth/2`. I've come to that based on standard border modifier implementation. – Bakyt Abdrasulov Dec 12 '22 at 06:36
  • 1
    Had to add a condition to draw all borders, for when the list is of size 1 :-) – Blundell Apr 16 '23 at 20:47
3

You can draw a border around the whole list, using the modifier border and a RoundedCornerShape:

LazyColumn(modifier.border(width = 1.dp, color = Color.Red, shape = RoundedCornerShape(1.dp)))

Or around every item by applying the same to the rows:

Row(modifier.border(width = 1.dp, color = Color.Green, shape = RoundedCornerShape(1.dp)))
Stephan
  • 15,704
  • 7
  • 48
  • 63
0

create a Surface with a rounded corner shape and then add the LazyColumn as a child inside the Surface. This will create a rounded background for the entire LazyColumn.

Surface(
            modifier = Modifier.fillMaxWidth(),
            color = MaterialTheme.colors.background,
            shape = RoundedCornerShape(10.dp)
        ) {
            LazyColumn(
                modifier = Modifier
                    .fillMaxWidth()
            ) {
                // Add your items here
            }
        }
Qamar khan
  • 179
  • 1
  • 7