Composable Function

Layout

[Layout] is the main core component for layout.

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

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

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.maxIntrinsicWidth(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)
}