{% setvar book_path %}/reference/kotlin/androidx/_book.yaml{% endsetvar %} {% include "_shared/_reference-head-tags.html" %}

SubcomposeIntermediateMeasureScope


SubcomposeIntermediateMeasureScope is the receiver scope for the intermediate measurer policy that gets invoked during the intermediate measure pass.

When in a LookaheadScope, SubcomposeLayout will be measured up to twice per frame. The two measurements will be using different measure policies and potentially different constraints.

The first measurement happens in the lookahead pass, where new layout is calculated based on the target constraints. Therefore, measurePolicy will receive the target constraints, and subcompose its content based on the target constraints. Note: Target constraints refers to the constraints that the SubcomposeLayout will receive once all the lookahead-based animations on size/constraints in the ancestor layouts have finished.

The second measurement is done in the intermediate measure pass after the lookahead pass. The intermediate measure pass allows adjustments to the measurement/placement using the pre-calculated layout information as animation targets to smooth over any any layout changes. In this measurement, intermediateMeasurePolicy will be invoked with the intermediate/animating constraints. By default, measure policy will be invoked in intermediateMeasurePolicy, and hence the same measure logic in measurePolicy with intermediate constraints will be used to measure and layout children in the intermediate pass.

Note: When measurePolicy is invoked in SubcomposeIntermediateMeasureScope, subcompose will simply retrieve the measurables associated with the given slot id based on the subcomposition during lookahead pass. This means if a given slot id has not been subcomposed in the lookahead pass, invoking subcompose during intermediate pass will result in an empty list.

import androidx.compose.runtime.Composable
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.unit.IntSize

// In this example, there is a custom modifier that animates the constraints and measures
// child with the animated constraints, as defined below.
// This modifier is built on top of `Modifier.intermediateLayout`, which
// allows access to the lookahead size of the layout. A resize animation will be kicked off
// whenever the lookahead size changes, to animate children from current size to lookahead size.
// Fixed constraints created based on the animation value will be used to measure
// child, so the child layout gradually changes its size and potentially its child's placement
// to fit within the animated constraints.
fun Modifier.animateConstraints() = composed {
    // Creates a size animation
    var sizeAnimation: Animatable<IntSize, AnimationVector2D>? by remember {
        mutableStateOf(null)
    }

    this.intermediateLayout { measurable, _ ->
        // When layout changes, the lookahead pass will calculate a new final size for the
        // child layout. This lookahead size can be used to animate the size
        // change, such that the animation starts from the current size and gradually
        // change towards `lookaheadSize`.
        if (lookaheadSize != sizeAnimation?.targetValue) {
            sizeAnimation?.run {
                launch { animateTo(lookaheadSize) }
            } ?: Animatable(lookaheadSize, IntSize.VectorConverter).let {
                sizeAnimation = it
            }
        }
        val (width, height) = sizeAnimation!!.value
        // Creates a fixed set of constraints using the animated size
        val animatedConstraints = Constraints.fixed(width, height)
        // Measure child with animated constraints.
        val placeable = measurable.measure(animatedConstraints)
        layout(placeable.width, placeable.height) {
            placeable.place(0, 0)
        }
    }
}

// In the example below, the SubcomposeLayout has a parent layout that animates its width
// between two fixed sizes using the `animateConstraints` modifier we created above.
@Composable
fun SubcomposeLayoutWithAnimatingParentLayout(
    isWide: Boolean,
    modifier: Modifier = Modifier,
    content: @Composable @UiComposable () -> Unit
) {
    // Create a MeasurePolicy to measure all children with incoming constraints and return the
    // largest width & height.
    val myMeasurePolicy = MeasurePolicy { measurables, constraints ->
        val placeables = measurables.map { it.measure(constraints) }
        val maxWidth = placeables.maxOf { it.width }
        val maxHeight = placeables.maxOf { it.height }
        layout(maxWidth, maxHeight) {
            placeables.forEach { it.place(0, 0) }
        }
    }
    Box(
        Modifier
            .requiredSize(if (isWide) 400.dp else 200.dp)
            .animateConstraints()
    ) {
        // SubcomposeLayout's measurePolicy will only be invoked with lookahead constraints.
        // The parent layout in this example is animating between two fixed widths. The
        // [measurePolicy] parameter will only be called with lookahead constraints
        // (i.e. constraints for 400.dp x 400.dp or 200.dp x 200.dp depending on the state.)
        // This may cause content lambda to jump to its final size. To create a smooth
        // experience, we need to remeasure the content with the intermediate
        // constraints created by the `animateConstraints` that we built above. Therefore, we
        // need to provide a [intermediateMeasurePolicy] to define how to measure the
        // content (using the measureables of the content that was composed in [measurePolicy])
        // with intermediate constraints.
        SubcomposeLayout(
            modifier,
            intermediateMeasurePolicy = { intermediateConstraints ->
                // Retrieve the measureables for slotId = Unit, and measure them with
                // intermediate constraints using the measurePolicy we created above.
                with(myMeasurePolicy) {
                    measure(
                        measurablesForSlot(Unit),
                        intermediateConstraints
                    )
                }
            },
            measurePolicy = { constraints ->
                val measurables = subcompose(Unit) { content() }
                with(myMeasurePolicy) { measure(measurables, constraints) }
            })
    }
}

Summary

Public functions

List<Measurable>

Returns the list of measureables associated with slotId that was subcomposed in the SubcomposeLayout's measurePolicy block during the lookahead pass.

Cmn
open List<Measurable>
subcompose(slotId: Any?, content: @Composable () -> Unit)

This function retrieves Measurables created for slotId based on the subcomposition that happened in the lookahead pass.

Cmn

Public properties

Constraints

Returns the Constraints used in the lookahead pass.

Cmn
SubcomposeMeasureScope.(Constraints) -> MeasureResult

This is the measure policy that is supplied to SubcomposeLayout in the measurePolicy parameter.

Cmn
IntSize

The size returned in the MeasureResult by the measurePolicy invoked during lookahead pass.

Cmn

Inherited functions

From androidx.compose.ui.unit.Density
open Int

Convert Dp to Int by rounding

Cmn
open Int

Convert Sp to Int by rounding

Cmn
open Dp

Convert Sp to Dp.

Cmn
open Dp

Convert an Int pixel value to Dp.

Cmn
open Dp

Convert a Float pixel value to a Dp

Cmn
open DpSize

Convert a Size to a DpSize.

Cmn
open Float

Convert Dp to pixels.

Cmn
open Float

Convert Sp to pixels.

Cmn
open Rect

Convert a DpRect to a Rect.

Cmn
open Size

Convert a DpSize to a Size.

Cmn
open TextUnit

Convert Dp to Sp.

Cmn
open TextUnit

Convert an Int pixel value to Sp.

Cmn
open TextUnit

Convert a Float pixel value to a Sp

Cmn
From androidx.compose.ui.layout.MeasureScope
open MeasureResult
layout(
    width: Int,
    height: Int,
    alignmentLines: Map<AlignmentLineInt>,
    placementBlock: Placeable.PlacementScope.() -> Unit
)

Sets the size and alignment lines of the measured layout, as well as the positioning block that defines the children positioning logic.

Cmn

Inherited properties

From androidx.compose.ui.unit.Density
Float

The logical density of the display.

Cmn
Float

Current user preference for the scaling factor for fonts.

Cmn
From androidx.compose.ui.layout.IntrinsicMeasureScope
open Boolean

This indicates whether the ongoing measurement is for lookahead pass.

Cmn
LayoutDirection

The LayoutDirection of the Layout or LayoutModifier using the measure scope to measure their children.

Cmn

Public functions

measurablesForSlot

fun measurablesForSlot(slotId: Any?): List<Measurable>

Returns the list of measureables associated with slotId that was subcomposed in the SubcomposeLayout's measurePolicy block during the lookahead pass. If the given slotId was not used in the subcomoposition, the returned list will be empty.

subcompose

open fun subcompose(slotId: Any?, content: @Composable () -> Unit): List<Measurable>

This function retrieves Measurables created for slotId based on the subcomposition that happened in the lookahead pass. If slotId was not subcomposed in the lookahead pass, subcompose will return an emptyList.

Public properties

lookaheadConstraints

val lookaheadConstraintsConstraints

Returns the Constraints used in the lookahead pass.

Note: Using this with lookaheadMeasurePolicy will effectively skip any intermediate stages from lookahead-based layout animations. Therefore it is recommended to use Constraints passed to intermediate measure policy to measure and layout children during intermediate pass. The only exception to that should be when some of the subcompositions are conditional. In that case, a custom intermediate measure policy should ideally be provided to SubcomposeLayout. Using lookaheadConstraints with lookaheadMeasurePolicy should be considered as the last resort.

lookaheadMeasurePolicy

val lookaheadMeasurePolicySubcomposeMeasureScope.(Constraints) -> MeasureResult

This is the measure policy that is supplied to SubcomposeLayout in the measurePolicy parameter. It is used in the lookahead pass, and it is also invoked by default in the intermediateMeasurePolicy for the intermediate measure pass.

During the intermediate pass, the lookaheadMeasurePolicy will receive potentially different (i.e. animating) constraints, and will subsequently remeasure and replace all children according to the new constraints.

Note: Intermediate measure pass will NOT run new subcompositions. subcompose calls in from the lookaheadMeasurePolicy in this pass will instead retrieve the measurables for the given slot based on the subcomposition from lookahead pass. In the rare case where slots are subcomposed conditionally dependent on constraints, it's recommended to provide to SubcomposeLayout a custom intermediate measure policy. A less desirable solution to this use case is to invoke lookaheadMeasurePolicy with lookaheadConstraints as its parameter, which will skip any intermediate stages straight to the lookahead sizes & positions.

lookaheadSize

val lookaheadSizeIntSize

The size returned in the MeasureResult by the measurePolicy invoked during lookahead pass.