Cilenco
Cilenco

Reputation: 7179

Custom layout ignores size of composeable

I'm currently playing around with custom Layouts in Jetpack Compose. This is what i got so far just for testing:

@Preview(widthDp = 1000, heightDp = 1000)
@Composable
fun MyLayout() {
    Layout({ Box(Modifier.size(48.dp).background(Color.Blue)) }) { measurables, constraints ->
        val placeables = measurables.map { it.measure(constraints) }
        layout(constraints.maxWidth, constraints.maxHeight) {
            placeables.forEach { it.place(0, 0) }
        }
    }
}

My problem here is that the Box fills the whole layout and not only it's size of 48.dp. Can someone explain to me why this is? I read here about creating custom layouts but couldn't find anything usefull.

Upvotes: 3

Views: 1920

Answers (2)

Jelle Fresen
Jelle Fresen

Reputation: 1946

The reason why the blue box fills the entire Layout is because of a combination of a few things: (1) you specify dimensions for the @Preview annotation, (2) you use the Layout's constraints unmodified to measure the box, and (3) the use of the size modifier.

Because of point 1, the constraints of the Layout will have both the minimum and maximum set to what was specified in the Preview dimensions. Because of point 2 those strict minimums and maximums are used to measure the box. And because of point 3 the incoming constraints take priority over the requested size (that's the way the size modifier works).

Mitigating each of those points will solve your problem.

As you already found out from one of the other answers, removing the width and height from the preview annotation will make those seem right. That is because without the width and height specified in the Preview annotation, the constraints will have a minimum of 0, so the child measurable are now allowed to take the size they want.

The recommended way though is to fix point 2: change the constraints to fit the requirements of your custom layout before you pass them to the measurables' measure methods. In this case, you probably want to create a copy of the constraints and set the minimum width and height to 0.

And finally you can fix point 3 by using requiredSize instead, which will size the measurable to the required size regardless, even though it will still communicate to its parents a size that's within the constraints. This is usually not recommended in production code because it makes sizing absolute, but for a preview it should be fine, and for tests it could even be desirable.

Upvotes: 0

Phil Dukhov
Phil Dukhov

Reputation: 88222

Layout size is determined by the size passed into layout(width, height).

Your Layout doesn't have any modifiers, that's why it has maxWidth/maxHeight constraints equal to available spacing. Using layout(constraints.maxWidth, constraints.maxHeight) has the same effect as applying Modifier.fillMaxSize. Actually with Modifier.fillMaxSize your'll set both max and min constraints to the available size.

To solve this you have two options:

  1. Apply Modifier.size to the Layout itself, instead of applying to the Box - in this case max/min constraints will be 48.dp

  2. Calculate layout size depending on placeable. The actual code depend on what layout do you expect to get. Your code looks like Box analog, so you need maximum of all placeable sizes:

    layout(placeables.maxOfOrNull { it.width } ?: 0, placeables.maxOfOrNull { it.height } ?: 0) {
    

Upvotes: 4

Related Questions