15

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
            }
        }
}

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
or_dvir
  • 479
  • 1
  • 7
  • 22
  • 1
    Try putting the verticalScroll modifier on the inner column. Since it is already filling the maxsize of the box, I don't see why you would want to put a vertical scroll on the box. – Rafsanjani Sep 30 '21 at 15:39
  • @Rafsanjani this fixes my specific issue so thanks, however my intention was also to understand why this is happening. so even though i used your solution, i have accepted Philip Dukhov's answer – or_dvir Oct 01 '21 at 10:16

4 Answers4

28

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:

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • 1
    i accepted this answer as it provides an explanation to why this was happening, however i actually used @Rafsanjani solution – or_dvir Oct 01 '21 at 10:18
6

You can wrap your layout into a BoxWithConstraints, which is not scrollable and grab the size from there:

BoxWithConstraints {
    val pageSize = this.maxHeight // already provided as Dp value
    Box(
        modifier = Modifier
            .fillMaxSize()
            .border(2.dp, Color.Green) //for visual effect only
            .verticalScroll(rememberScrollState())
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .height(pageSize)
                .padding(2.dp)
                .border(2.dp, Color.Red) //for visual effect only
        ) {
            //some content
        }
        // just so we can scroll a bit further
        Spacer(modifier = Modifier.padding(top = pageSize).fillMaxWidth().height(72.dp))
    }
}
Adrian K
  • 3,942
  • 12
  • 15
  • not a terrible idea, even though it feels a little bit like a hack. but for my specific case @Rafsanjani solution works and feels more "clean" so i went with that – or_dvir Oct 01 '21 at 10:10
4

I know its late, but this seems to be working for me.

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

This expanded the height to its maximum intrinsic size and also it is vertically scrollable.

1

The solution with BoxWithConstraints pointed me to a working solution (in my case).

I wrap the Column in a max sized BoxWithConstraints. Then I set the height of the Column to Modifier.heightIn(min=maxHeight). This way the column is at least "maxHeight" tall.

BoxWithConstraints(
        modifier = Modifier
            .fillMaxSize()
    ) {
        Column(
            modifier = Modifier
                .heightIn(min = maxHeight)
                .verticalScroll(rememberScrollState())
        ) {
            Spacer(modifier = Modifier.weight(1f)
            Text(text="This is at the bottom")
        }
    }
Pepijn
  • 1,439
  • 17
  • 16