'Why Surface child compasable fill max size if i don't wrap it in an other container?

This code fills the full screen if i specify the size to be 100.dp.

ComposeTheme {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colors.background
    ) {
        Box(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .clip(RoundedCornerShape(12.dp))
                .background(color = Color.Red)
        ) {

        }
    }
}

This code behave properly by filling the required size.

ComposeTheme {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colors.background
    ) {
        Column(modifier = Modifier.fillMaxSize()) {
            Box(
                modifier = Modifier
                    .width(100.dp)
                    .height(100.dp)
                    .clip(RoundedCornerShape(12.dp))
                    .background(color = Color.Red)
            ) {

            }
        }

    }
}

Can somebody please explain why is it happening?



Solution 1:[1]

This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.

As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.

Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.

Here is how one of the maintainers explains the reasons for this decision:

Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.

It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pylyp Dukhov