androidandroid-jetpack-composeandroid-jetpack-compose-material3

How to make a circular Box adapt its size to its Text content in Jetpack Compose?


I'm trying to create a circular Box in Jetpack Compose that dynamically sizes itself to fit the Text content it holds. The goal is for the circle to be just big enough to contain the text, taking into account that the minimum desired is 24.dp.

This is my code:

@Composable
fun MyText() {
    Box(
        modifier = Modifier
            .sizeIn(minWidth = 24.dp, minHeight = 24.dp)
            .wrapContentSize()
            .aspectRatio(1f)
            .background(Color.Blue, shape = CircleShape),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text =  "1",
            color = Color.White,
            style = MaterialTheme.typography.bodyMedium,
        )
    }
}

Right now the box is not adapted to the content. This is the obtained result:

Image


Solution

  • The aspectRatio Modifier seems not to work here because it first checks the maxWidth and maxHeight constraints, but in your case you need minWidth and minHeight to be checked first.

    Please try to use the layout Modifier to adjust the measurement and placement process like this:

    @Composable
    fun MyText() {
    
        val minSizePx = with(LocalDensity.current) { 24.dp.roundToPx() }
    
        Box(
            modifier = Modifier
                .background(Color.Blue, shape = CircleShape)
                .layout { measurable, constraints ->
                    val minConstraints = constraints.copy(minWidth = minSizePx, minHeight = minSizePx)
                    val childNode = measurable.measure(minConstraints)
                    val squareSize = max(childNode.height, childNode.width)
    
                    layout(squareSize, squareSize) {
                        val xOffset = squareSize - childNode.width
                        val yOffset = squareSize - childNode.height
                        childNode.place(xOffset / 2, yOffset / 2)
                    }
                },
            contentAlignment = Alignment.Center
        ) {
            Text(
                text =  "1",
                color = Color.White,
                style = MaterialTheme.typography.bodyMedium,
            )
        }
    }
    

    Output

    Screenshot

    Alternatives

    You could also write a custom Modifier as described in this article and also mentioned in this answer.