I want to draw a border around a Text like this,
Text("Box around text",
modifier = Modifier
.padding(top = 8.dp)
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray))
Text("Box around text with a very very very very longlonglonglongword",
modifier = Modifier
.padding(top = 8.dp)
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray)
)
But in the case of a multiline text, it doesn't look well.
There is a gap on the right between the border and the text.
So how to draw a border around a multiline text, so that it fits the text width?
I'm not sure if this is a bug, or an expected behaviour. For the first case I've created this issue, we'll see what the maintainers think.
Here's how you can restrict it manually:
var textWidth by remember { mutableStateOf<Int?>(null) }
Text(
"Box around text with a very very very very longlonglonglongword",
color = Color.White,
onTextLayout = { layoutResult ->
textWidth = (0 until layoutResult.lineCount)
.maxOf {
ceil(layoutResult.getLineRight(it)).toInt()
}
},
modifier = Modifier
.border(width = 2.dp, color = Color.Red)
.width(with(LocalDensity.current) { textWidth?.toDp() ?: Dp.Unspecified })
.drawWithContent {
// prevent full with text from being drawn
if (textWidth != null) {
drawContent()
}
}
.background(Color.DarkGray)
)
Result:

The disadvantage of the first solution is that it takes one frame to calculate the width. We hide the text itself, but if there is another view on the right side, it may appear in a wrong position at first. The solution below is more cumbersome, but does all the work during one recomposition to prevent such a problem.
SubcomposeLayout { constraints ->
val composable = @Composable { onTextLayout: (TextLayoutResult) -> Unit ->
Text(
"Box around text with a very very very very longlonglonglongword",
color = Color.White,
onTextLayout = onTextLayout,
modifier = Modifier
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray)
)
}
var textWidth: Int? = null
subcompose("measureView") {
composable { layoutResult ->
textWidth = (0 until layoutResult.lineCount)
.maxOf {
ceil(layoutResult.getLineRight(it)).toInt()
}
}
}[0].measure(constraints)
val placeable = subcompose("content") {
composable { }
}[0].measure(constraints.copy(maxWidth = textWidth!!))
layout(width = textWidth!!, height = placeable.height) {
placeable.place(0, 0)
}
}