Layout

Composable Function

Common
@Composable
inline fun Layout(
    content: @Composable @UiComposable () -> Unit,
    modifier: Modifier = Modifier,
    measurePolicy: MeasurePolicy,
)

Layout is the main core component for layout. It can be used to measure and position zero or more layout children.

The measurement, layout and intrinsic measurement behaviours of this layout will be defined by the measurePolicy instance. See MeasurePolicy for more details.

For a composable able to define its content according to the incoming constraints, see androidx.compose.foundation.layout.BoxWithConstraints.

Example usage with custom intrinsic measurements:

Parameters

contentThe children composable to be laid out.
modifierModifiers to be applied to the layout.
measurePolicyThe policy defining the measurement and positioning of the layout.
Common
@Composable
inline fun Layout(modifier: Modifier = Modifier, measurePolicy: MeasurePolicy)

Layout is the main core component for layout for "leaf" nodes. It can be used to measure and position zero children.

The measurement, layout and intrinsic measurement behaviours of this layout will be defined by the measurePolicy instance. See MeasurePolicy for more details.

For a composable able to define its content according to the incoming constraints, see androidx.compose.foundation.layout.BoxWithConstraints.

Example usage with custom intrinsic measurements:

Parameters

modifierModifiers to be applied to the layout.
measurePolicyThe policy defining the measurement and positioning of the layout.
Common
@Composable
inline fun Layout(
    contents: List<@Composable @UiComposable () -> Unit>,
    modifier: Modifier = Modifier,
    measurePolicy: MultiContentMeasurePolicy,
)

Layout is the main core component for layout. It can be used to measure and position zero or more layout children.

This overload accepts a list of multiple composable content lambdas, which allows treating measurables put into different content lambdas differently - measure policy will provide a list of lists of Measurables, not just a single list. Such list has the same size as the list of contents passed into Layout and contains the list of measurables of the corresponding content lambda in the same order.

Note that layouts emitted as part of all contents lambdas will be added as a direct children for this Layout. This means that if you set a custom z index on some children, the drawing order will be calculated as if they were all provided as part of one lambda.

Parameters

contentsThe list of children composable contents to be laid out.
modifierModifiers to be applied to the layout.
measurePolicyThe policy defining the measurement and positioning of the layout.

Code Examples

LayoutUsage

@Composable
fun LayoutUsage(content: @Composable () -> Unit) {
    // We build a layout that will occupy twice as much space as its children,
    // and will position them to be bottom right aligned.
    Layout(content) { measurables, constraints ->
        // measurables contains one element corresponding to each of our layout children.
        // constraints are the constraints that our parent is currently measuring us with.
        val childConstraints =
            Constraints(
                minWidth = constraints.minWidth / 2,
                minHeight = constraints.minHeight / 2,
                maxWidth =
                    if (constraints.hasBoundedWidth) {
                        constraints.maxWidth / 2
                    } else {
                        Constraints.Infinity
                    },
                maxHeight =
                    if (constraints.hasBoundedHeight) {
                        constraints.maxHeight / 2
                    } else {
                        Constraints.Infinity
                    },
            )
        // We measure the children with half our constraints, to ensure we can be double
        // the size of the children.
        val placeables = measurables.map { it.measure(childConstraints) }
        val layoutWidth = (placeables.maxByOrNull { it.width }?.width ?: 0) * 2
        val layoutHeight = (placeables.maxByOrNull { it.height }?.height ?: 0) * 2
        // We call layout to set the size of the current layout and to provide the positioning
        // of the children. The children are placed relative to the current layout place.
        layout(layoutWidth, layoutHeight) {
            placeables.forEach {
                it.placeRelative(layoutWidth - it.width, layoutHeight - it.height)
            }
        }
    }
}

LayoutWithProvidedIntrinsicsUsage

@Composable
fun LayoutWithProvidedIntrinsicsUsage(content: @Composable () -> Unit) {
    // We build a layout that will occupy twice as much space as its children,
    // and will position them to be bottom right aligned.
    val measurePolicy =
        object : MeasurePolicy {
            override fun MeasureScope.measure(
                measurables: List<Measurable>,
                constraints: Constraints,
            ): MeasureResult {
                // measurables contains one element corresponding to each of our layout children.
                // constraints are the constraints that our parent is currently measuring us with.
                val childConstraints =
                    Constraints(
                        minWidth = constraints.minWidth / 2,
                        minHeight = constraints.minHeight / 2,
                        maxWidth =
                            if (constraints.hasBoundedWidth) {
                                constraints.maxWidth / 2
                            } else {
                                Constraints.Infinity
                            },
                        maxHeight =
                            if (constraints.hasBoundedHeight) {
                                constraints.maxHeight / 2
                            } else {
                                Constraints.Infinity
                            },
                    )
                // We measure the children with half our constraints, to ensure we can be double
                // the size of the children.
                val placeables = measurables.map { it.measure(childConstraints) }
                val layoutWidth = (placeables.maxByOrNull { it.width }?.width ?: 0) * 2
                val layoutHeight = (placeables.maxByOrNull { it.height }?.height ?: 0) * 2
                // We call layout to set the size of the current layout and to provide the
                // positioning
                // of the children. The children are placed relative to the current layout place.
                return layout(layoutWidth, layoutHeight) {
                    placeables.forEach {
                        it.placeRelative(layoutWidth - it.width, layoutHeight - it.height)
                    }
                }
            }
            // The min intrinsic width of this layout will be twice the largest min intrinsic
            // width of a child. Note that we call minIntrinsicWidth with h / 2 for children,
            // since we should be double the size of the children.
            override fun IntrinsicMeasureScope.minIntrinsicWidth(
                measurables: List<IntrinsicMeasurable>,
                height: Int,
            ) = (measurables.map { it.minIntrinsicWidth(height / 2) }.maxByOrNull { it } ?: 0) * 2
            override fun IntrinsicMeasureScope.minIntrinsicHeight(
                measurables: List<IntrinsicMeasurable>,
                width: Int,
            ) = (measurables.map { it.minIntrinsicHeight(width / 2) }.maxByOrNull { it } ?: 0) * 2
            override fun IntrinsicMeasureScope.maxIntrinsicWidth(
                measurables: List<IntrinsicMeasurable>,
                height: Int,
            ) = (measurables.map { it.maxIntrinsicHeight(height / 2) }.maxByOrNull { it } ?: 0) * 2
            override fun IntrinsicMeasureScope.maxIntrinsicHeight(
                measurables: List<IntrinsicMeasurable>,
                width: Int,
            ) = (measurables.map { it.maxIntrinsicHeight(width / 2) }.maxByOrNull { it } ?: 0) * 2
        }
    Layout(content = content, measurePolicy = measurePolicy)
}

LayoutWithMultipleContentsUsage

@Composable
fun LayoutWithMultipleContentsUsage(
    content1: @Composable () -> Unit,
    content2: @Composable () -> Unit,
) {
    // We can provide pass a list of two composable lambdas in order to be able to treat
    // measureables from each lambda differently.
    Layout(listOf(content1, content2)) { (content1Measurables, content2Measurables), constraints ->
        val content1Placeables = content1Measurables.map { it.measure(constraints) }
        val content2Placeables = content2Measurables.map { it.measure(constraints) }
        layout(constraints.maxWidth, constraints.maxHeight) {
            var currentX = 0
            var currentY = 0
            var currentMaxHeight = 0
            // we place placeables from content1 as a first line
            content1Placeables.forEach {
                it.place(currentX, currentY)
                currentX += it.width
                currentMaxHeight = maxOf(currentMaxHeight, it.height)
            }
            currentX = 0
            currentY = currentMaxHeight
            // and placeables from content2 composable as a second line
            content2Placeables.forEach {
                it.place(currentX, currentY)
                currentX += it.width
            }
        }
    }
}