androidkotlinandroid-jetpack-compose

fillMaxSize modifier not working when combined with VerticalScroll in Jetpack Compose


I tried looking this up but could not find anything relevant.

I want to have a "full size" Column inside a vertically scrollable Box and this combination just doesn't seem to work. when I add the verticalScroll(rememberScrollState()) modifier to the Box, it seems to "disable" the fillMaxSize() modifier of the Column

The following code does not work as expected:

MyTheme {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .border(2.dp, Color.Green) //for visual effect only
                .verticalScroll(rememberScrollState())
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(2.dp)
                    .border(2.dp, Color.Red) //for visual effect only
            ) {
               //some content
            }
        }
}

Expected result: Both Box and Column (green and red borders) fill the entire screen.

Actual result: Box fills the screen, but Column does not fill height

However if I remove the verticalScroll() modifier from the Box, I get the expected result:

MyTheme {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .border(2.dp, Color.Green) //for visual effect only
                //verticalScroll modifier removed
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(2.dp)
                    .border(2.dp, Color.Red) //for visual effect only
            ) {
               //some content
            }
        }
}


Solution

  • As @AjayChandran noted, the simples solution is to add Modifier.height(IntrinsicSize.Max) after verticalScroll modifier.

    This solution is simple, but may cause performance issues, since all the measurements inside the view are gonna be done twice. If you have performance problems in prod app version, consider using constant height - see detailed explanation below.

    Modifier
        .verticalScroll(scrollState)
        .height(IntrinsicSize.Max)
    

    verticalScroll wraps the height of the content, and can be stretched to very long, so the scope has Constraints.Infinity for maxHeight constraint.

    From fillMaxHeight documentation

    If the incoming maximum height is Constraints.Infinity this modifier will have no effect.

    That's why you need to set height explicitly.

    Consider switching to LazyColumn(which has fillParentMaxHeight() for this exact purpose) or to Pager(which is made explicitly for such cases).

    Also, as @AdrianK pointer out, with regular scrollable you can wrap your view with BoxWithConstraints, and use maxHeight to set height of your view.

    BoxWithConstraints {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .border(2.dp, Color.Green)
                .verticalScroll(rememberScrollState())
        ) {
            Column {
                repeat(2) {
                    Column(
                        modifier = Modifier
                            // fillMaxWidth instead of fillMaxSize
                            .fillMaxWidth()
                            // explicit height modifier
                            .height(this@BoxWithConstraints.maxHeight)
                            .padding(2.dp)
                            .border(2.dp, Color.Red)
                    ) {
                        //some content
                    }
                }
            }
        }
    }
    

    Result: