Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make multi-line text wrap width in Compose?

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.

box around text

So how to draw a border around a multiline text, so that it fits the text width?

like image 260
netimen Avatar asked Dec 08 '25 08:12

netimen


1 Answers

It looks like a bug, I've created this issue.

Here's how you can restrict it manually.

Note that using SubcomposeLayout can affect performance - alternatively, you could do the same trick by storing the width in mutableStateOf - this would be more performant, but would also make the view flicker, since within one frame a view with an unconstrained width would be rendered (or empty space of the same size if you apply zero alpha).

@Composable
fun WrapTextContent(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current,
) {
    SubcomposeLayout(modifier) { constraints ->
        val composable = @Composable { localOnTextLayout: (TextLayoutResult) -> Unit ->
            Text(
                text = text,
                color = color,
                fontSize = fontSize,
                fontStyle = fontStyle,
                fontWeight = fontWeight,
                fontFamily = fontFamily,
                letterSpacing = letterSpacing,
                textDecoration = textDecoration,
                textAlign = textAlign,
                lineHeight = lineHeight,
                overflow = overflow,
                softWrap = softWrap,
                maxLines = maxLines,
                onTextLayout = localOnTextLayout,
                style = style,
            )
        }
        var textWidthOpt: Int? = null
        subcompose("measureView") {
            composable { layoutResult ->
                textWidthOpt = (0 until layoutResult.lineCount)
                    .maxOf { line ->
                        ceil(layoutResult.getLineRight(line) - layoutResult.getLineLeft(line)).toInt()
                    }
            }
        }[0].measure(constraints)
        val textWidth = textWidthOpt!!
        val placeable = subcompose("content") {
            composable(onTextLayout)
        }[0].measure(constraints.copy(minWidth = textWidth, maxWidth = textWidth))

        layout(width = textWidth, height = placeable.height) {
            placeable.place(0, 0)
        }
    }
}

Usage:


var textWidth by remember { mutableStateOf<Int?>(null) }
WrapTextContent(
    "Box around text with a very very very very longlonglonglongword",
    color = Color.White,
    modifier = Modifier
        .border(width = 2.dp, color = Color.Red)
        .background(Color.DarkGray)
)

Result:

like image 74
Philip Dukhov Avatar answered Dec 10 '25 00:12

Philip Dukhov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!