Search code examples
androidkotlinandroid-jetpack-compose

How to wrap content height correctly in Compose?


How to wrap content height of a Column that has various Composables, one of them being another Column? I have a Card with a Column with 3 items, a title, a body (Column with n texts and scroll) and a bottom text. The idea is if the Card has more height than the screen, then, the user will have the possibility to scroll the body (body has a scroll). The problem is that if the body has a lot of content (filling the entire screen height), the bottom text of the card is not displayed.

With the next sample you can click to enlarge or shorten the text to test the issue.

var long by remember{ mutableStateOf(true) }
Card(modifier = modifier.widthIn(max = 400.dp).fillMaxWidth().padding(16.dp),
    onClick = { long = !long }
) {
    Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
            Text(text = "1")
            Text(text = "2")

            val scrollState = rememberScrollState()
            Column(modifier = Modifier.verticalScroll(scrollState)) {
                if (long)
                    repeat(50) { Text(text = "body") }
                else
                    repeat(3) { Text(text = "body") }
            }

            Text(text = "3")
        }
    }
}

I tryed using wrapContentHeight on the parent column (same result), also tryed using weight(1f) in the child column (then the bottom text is visible always but the card is always stretched to full screen height), etc... can't get the desired behaviour.


Solution

  • Assign Modifier.weight(1f, fill = false) to scrollable Column.

    Column(
        modifier = Modifier
            .weight(1f, fill = false)
            .verticalScroll(scrollState)
    ) {
        if (long)
            repeat(50) { Text(text = "body") }
        else
            repeat(3) { Text(text = "body") }
    }
    

    weight Modifier in Row or Column causes Composables with this Modifier to be measured after other Composables and set space is divided between the ones with weight Modifier based on their weight fractions.

    If you set fill = false they are measured after other Composables still, but they occupy spaces as big as their content most, not available space all the time, after other other Composables are measured.

    Size the element's height proportional to its weight relative to other weighted sibling elements in the Column. The parent will divide the vertical space remaining after measuring unweighted child elements and distribute it according to this weight. When fill is true, the element will be forced to occupy the whole height allocated to it. Otherwise, the element is allowed to be smaller - this will result in Column being smaller, as the unused allocated height will not be redistributed to other siblings.