[go: nahoru, domu]

Remove intermediate MeasurePolicy APIs from SubcomposeLayout

Test: updated existing tests
RelNote: "Removed SubcomposeLayout APIs using `intermediateMeassurePolicy`
in favor of bifercating the measure logic in the same `measurePolicy`
based on `MeasureScope.isLookingAhead`."

Change-Id: Idc6addb96070d7bcafa542baf621c31d5809e6e1
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 6b29580..0f4361b 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2362,22 +2362,9 @@
     method @androidx.compose.runtime.Stable public static operator long times(long, long size);
   }
 
-  @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface SubcomposeIntermediateMeasureScope extends androidx.compose.ui.layout.SubcomposeMeasureScope {
-    method public long getLookaheadConstraints();
-    method public kotlin.jvm.functions.Function2<androidx.compose.ui.layout.SubcomposeMeasureScope,androidx.compose.ui.unit.Constraints,androidx.compose.ui.layout.MeasureResult> getLookaheadMeasurePolicy();
-    method public long getLookaheadSize();
-    method public java.util.List<androidx.compose.ui.layout.Measurable> measurablesForSlot(Object? slotId);
-    method public default java.util.List<androidx.compose.ui.layout.Measurable> subcompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    property public abstract long lookaheadConstraints;
-    property public abstract kotlin.jvm.functions.Function2<androidx.compose.ui.layout.SubcomposeMeasureScope,androidx.compose.ui.unit.Constraints,androidx.compose.ui.layout.MeasureResult> lookaheadMeasurePolicy;
-    property public abstract long lookaheadSize;
-  }
-
   public final class SubcomposeLayoutKt {
     method @androidx.compose.runtime.Composable public static void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public static void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeIntermediateMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> intermediateMeasurePolicy, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
     method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void SubcomposeLayout(androidx.compose.ui.layout.SubcomposeLayoutState state, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void SubcomposeLayout(androidx.compose.ui.layout.SubcomposeLayoutState state, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeIntermediateMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> intermediateMeasurePolicy, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
     method public static androidx.compose.ui.layout.SubcomposeSlotReusePolicy SubcomposeSlotReusePolicy(int maxSlotsToRetainForReuse);
   }
 
@@ -2385,9 +2372,7 @@
     ctor public SubcomposeLayoutState(androidx.compose.ui.layout.SubcomposeSlotReusePolicy slotReusePolicy);
     ctor public SubcomposeLayoutState();
     ctor @Deprecated public SubcomposeLayoutState(int maxSlotsToRetainForReuse);
-    method @androidx.compose.ui.ExperimentalComposeUiApi public boolean isInLookaheadScope();
     method public androidx.compose.ui.layout.SubcomposeLayoutState.PrecomposedSlotHandle precompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    property @androidx.compose.ui.ExperimentalComposeUiApi public final boolean isInLookaheadScope;
   }
 
   public static interface SubcomposeLayoutState.PrecomposedSlotHandle {
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/SubcomposeLayoutSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/SubcomposeLayoutSample.kt
index 9c932f2..b116741 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/SubcomposeLayoutSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/SubcomposeLayoutSample.kt
@@ -17,27 +17,9 @@
 package androidx.compose.ui.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector2D
-import androidx.compose.animation.core.VectorConverter
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.UiComposable
-import androidx.compose.ui.composed
-import androidx.compose.ui.layout.MeasurePolicy
 import androidx.compose.ui.layout.SubcomposeLayout
-import androidx.compose.ui.layout.intermediateLayout
-import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.launch
 
 @Sampled
 @Composable
@@ -67,97 +49,4 @@
     }
 }
 
-enum class SlotsEnum { Main, Dependent }
-
-@OptIn(ExperimentalComposeUiApi::class)
-@Sampled
-fun SubcomposeLayoutWithIntermediateMeasurePolicySample() {
-    // 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) }
-                })
-        }
-    }
-}
\ No newline at end of file
+enum class SlotsEnum { Main, Dependent }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
index 378d5b7..942c141 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
@@ -196,7 +196,7 @@
                         Box(Modifier.fillMaxSize())
                     }[0].measure(constraints)
                     val size = placeable.run { IntSize(width, height) }
-                    if (this is SubcomposeIntermediateMeasureScope) {
+                    if (!isLookingAhead) {
                         actualSize = size
                     } else {
                         actualTargetSize = size
@@ -589,30 +589,12 @@
 
     @Test
     fun defaultMeasurePolicyInSubcomposeLayout() {
-        var actualLookaheadSize by mutableStateOf(IntSize.Zero)
         var defaultIntermediateMeasureSize by mutableStateOf(IntSize.Zero)
         rule.setContent {
             CompositionLocalProvider(LocalDensity provides Density(1f)) {
                 LookaheadScope {
                     SubcomposeLayout(
                         Modifier
-                            .fillMaxSize()
-                            .requiredSize(200.dp),
-                        intermediateMeasurePolicy = { constraints ->
-                            measurablesForSlot(Unit)[0].measure(constraints)
-                            actualLookaheadSize = this.lookaheadSize
-                            layout(0, 0) {}
-                        }
-                    ) { constraints ->
-                        val placeable = subcompose(Unit) {
-                            Box(Modifier.requiredSize(400.dp, 600.dp))
-                        }[0].measure(constraints)
-                        layout(500, 300) {
-                            placeable.place(0, 0)
-                        }
-                    }
-                    SubcomposeLayout(
-                        Modifier
                             .size(150.dp)
                             .intermediateLayout { measurable, _ ->
                                 measurable
@@ -636,7 +618,6 @@
             }
         }
         rule.runOnIdle {
-            assertEquals(IntSize(500, 300), actualLookaheadSize)
             assertEquals(IntSize(500, 300), defaultIntermediateMeasureSize)
         }
     }
@@ -1719,12 +1700,18 @@
             mutableStateListOf<Int>().apply { addAll(expectedPlacementOrder1) }
 
         var iteration by mutableStateOf(0)
+        var lookaheadConstraints by mutableStateOf<Constraints?>(null)
         // Expect the default placement to be the same as lookahead
         rule.setContent {
             LookaheadScope {
-                SubcomposeLayout(
-                    intermediateMeasurePolicy = { lookaheadMeasurePolicy(lookaheadConstraints) }
-                ) { constraints ->
+                SubcomposeLayout { incomingConstraints ->
+                    val constraints = if (isLookingAhead) {
+                        lookaheadConstraints = incomingConstraints
+                        incomingConstraints
+                    } else {
+                        lookaheadConstraints!!
+                    }
+
                     val placeables = mutableListOf<Placeable>()
                     repeat(3) { id ->
                         subcompose(id) {
@@ -1975,20 +1962,32 @@
                         }
                         SubcomposeLayout(
                             Modifier
-                                .fillMaxSize()
-                                .requiredSize(200.dp),
-                            intermediateMeasurePolicy = { constraints ->
-                                assertFalse(isLookingAhead)
-                                measurablesForSlot(Unit)[0].measure(constraints)
-                                layout(0, 0) {}
-                            }
+                                .layout { measurable, constraints ->
+                                    measurable.measure(constraints).run {
+                                        if (isLookingAhead) {
+                                            assertEquals(500, width)
+                                            assertEquals(300, height)
+                                        } else {
+                                            assertEquals(100, width)
+                                            assertEquals(120, height)
+                                        }
+                                        layout(width, height) {
+                                            place(0, 0)
+                                        }
+                                    }
+                                }
                         ) { constraints ->
-                            assertTrue(isLookingAhead)
                             val placeable = subcompose(Unit) {
                                 Box(Modifier.requiredSize(400.dp, 600.dp))
                             }[0].measure(constraints)
-                            layout(500, 300) {
-                                placeable.place(0, 0)
+                            if (isLookingAhead) {
+                                layout(500, 300) {
+                                    placeable.place(0, 0)
+                                }
+                            } else {
+                                layout(100, 120) {
+                                    placeable.place(0, 0)
+                                }
                             }
                         }
                     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index d95d81b..619812e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -44,7 +44,6 @@
 import androidx.compose.ui.node.requireOwner
 import androidx.compose.ui.platform.createSubcomposition
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 
 /**
@@ -83,155 +82,6 @@
  * for example to use the values calculated during the measurement as params for the composition
  * of the children.
  *
- * 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, [measurePolicy] 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 the intermediate pass, `subcompose` will simply
- * return 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.
- *
- * Possible use cases:
- * * You need to know the constraints passed by the parent during the composition and can't solve
- * your use case with just custom [Layout] or [LayoutModifier].
- * See [androidx.compose.foundation.layout.BoxWithConstraints].
- * * You want to use the size of one child during the composition of the second child.
- * * You want to compose your items lazily based on the available size. For example you have a
- * list of 100 items and instead of composing all of them you only compose the ones which are
- * currently visible(say 5 of them) and compose next items when the component is scrolled.
- *
- * @sample androidx.compose.ui.samples.SubcomposeLayoutWithIntermediateMeasurePolicySample
- *
- * @param modifier [Modifier] to apply for the layout.
- * @param intermediateMeasurePolicy A measure policy that will be invoked during the intermediate
- *                                  measure pass.
- * @param measurePolicy Measure policy which provides ability to subcompose during the measuring.
- */
-@ExperimentalComposeUiApi
-@Composable
-fun SubcomposeLayout(
-    modifier: Modifier = Modifier,
-    intermediateMeasurePolicy:
-    (SubcomposeIntermediateMeasureScope.(Constraints) -> MeasureResult) = { constraints ->
-        lookaheadMeasurePolicy(constraints)
-    },
-    measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
-) {
-    SubcomposeLayout(
-        state = remember { SubcomposeLayoutState() },
-        modifier = modifier,
-        intermediateMeasurePolicy = intermediateMeasurePolicy,
-        measurePolicy = measurePolicy
-    )
-}
-
-/**
- * [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.
- *
- * @sample androidx.compose.ui.samples.SubcomposeLayoutWithIntermediateMeasurePolicySample
- */
-@ExperimentalComposeUiApi
-sealed interface SubcomposeIntermediateMeasureScope : SubcomposeMeasureScope {
-    /**
-     * 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.
-     */
-    fun measurablesForSlot(slotId: Any?): List<Measurable>
-
-    /**
-     * The size returned in the [MeasureResult] by the measurePolicy invoked during lookahead pass.
-     */
-    val lookaheadSize: IntSize
-
-    /**
-     * 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.
-     */
-    val lookaheadMeasurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
-
-    /**
-     * 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.
-     */
-    val lookaheadConstraints: Constraints
-
-    /**
-     * This function retrieves [Measurable]s 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].
-     */
-    override fun subcompose(slotId: Any?, content: @Composable () -> Unit): List<Measurable> =
-        measurablesForSlot(slotId)
-}
-
-/**
- * Analogue of [Layout] which allows to subcompose the actual content during the measuring stage
- * for example to use the values calculated during the measurement as params for the composition
- * of the children.
- *
  * Possible use cases:
  * * You need to know the constraints passed by the parent during the composition and can't solve
  * your use case with just custom [Layout] or [LayoutModifier].
@@ -254,66 +104,6 @@
     modifier: Modifier = Modifier,
     measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
 ) {
-    @OptIn(ExperimentalComposeUiApi::class)
-    SubcomposeLayout(state, modifier, { lookaheadMeasurePolicy(it) }, measurePolicy)
-}
-
-/**
- * Analogue of [Layout] which allows to subcompose the actual content during the measuring stage
- * for example to use the values calculated during the measurement as params for the composition
- * of the children.
- *
- * 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, [measurePolicy] 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 the intermediate pass, `subcompose` will simply
- * return 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.
- *
- * Possible use cases:
- * * You need to know the constraints passed by the parent during the composition and can't solve
- * your use case with just custom [Layout] or [LayoutModifier].
- * See [androidx.compose.foundation.layout.BoxWithConstraints].
- * * You want to use the size of one child during the composition of the second child.
- * * You want to compose your items lazily based on the available size. For example you have a
- * list of 100 items and instead of composing all of them you only compose the ones which are
- * currently visible(say 5 of them) and compose next items when the component is scrolled.
- *
- * @param state the state object to be used by the layout.
- * @param modifier [Modifier] to apply for the layout.
- * @param intermediateMeasurePolicy A measure policy that will be invoked during the intermediate
- *                                  measure pass.
- * @param measurePolicy Measure policy which provides ability to subcompose during the measuring.
- */
-@Composable
-@UiComposable
-@ExperimentalComposeUiApi
-fun SubcomposeLayout(
-    state: SubcomposeLayoutState,
-    modifier: Modifier = Modifier,
-    intermediateMeasurePolicy:
-    (SubcomposeIntermediateMeasureScope.(Constraints) -> MeasureResult) = { constraints ->
-        lookaheadMeasurePolicy(constraints)
-    },
-    measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
-) {
     val compositionContext = rememberCompositionContext()
     val materialized = currentComposer.materialize(modifier)
     val localMap = currentComposer.currentCompositionLocalMap
@@ -323,10 +113,6 @@
             set(state, state.setRoot)
             set(compositionContext, state.setCompositionContext)
             set(measurePolicy, state.setMeasurePolicy)
-            set(
-                intermediateMeasurePolicy,
-                state.setIntermediateMeasurePolicy
-            )
             set(localMap, ComposeUiNode.SetResolvedCompositionLocals)
             set(materialized, ComposeUiNode.SetModifier)
         }
@@ -359,6 +145,11 @@
      * used during the previous measuring.
      * @param content the composable content which defines the slot. It could emit multiple
      * layouts, in this case the returned list of [Measurable]s will have multiple elements.
+     * **Note:** When a [SubcomposeLayout] is in a [LookaheadScope], the subcomposition only
+     * happens during the lookahead pass. In the post-lookahead/main pass, [subcompose] will
+     * return the list of [Measurable]s that were subcomposed during the lookahead pass. If the
+     * structure of the subtree emitted from [content] is dependent on incoming constraints,
+     * consider using constraints received from the lookahead pass for both passes.
      */
     fun subcompose(slotId: Any?, content: @Composable () -> Unit): List<Measurable>
 }
@@ -368,7 +159,6 @@
  *
  * [slotReusePolicy] the policy defining what slots should be retained to be reused later.
  */
-@OptIn(ExperimentalComposeUiApi::class)
 class SubcomposeLayoutState(
     private val slotReusePolicy: SubcomposeSlotReusePolicy
 ) {
@@ -396,16 +186,6 @@
         SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse)
     )
 
-    /**
-     * Returns whether the [SubcomposeLayout] is in a [LookaheadScope]. Intermediate measure policy
-     * will be only invoked when in a [LookaheadScope].
-     */
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @get:ExperimentalComposeUiApi
-    @ExperimentalComposeUiApi
-    val isInLookaheadScope: Boolean
-        get() = state.isInLookaheadScope
-
     private var _state: LayoutNodeSubcompositionsState? = null
     private val state: LayoutNodeSubcompositionsState
         get() = requireNotNull(_state) {
@@ -428,10 +208,6 @@
         LayoutNode.((SubcomposeMeasureScope.(Constraints) -> MeasureResult)) -> Unit =
         { measurePolicy = state.createMeasurePolicy(it) }
 
-    internal val setIntermediateMeasurePolicy:
-        LayoutNode.(SubcomposeIntermediateMeasureScope.(Constraints) -> MeasureResult) -> Unit =
-        { state.intermediateMeasurePolicy = it }
-
     /**
      * Composes the content for the given [slotId]. This makes the next scope.subcompose(slotId)
      * call during the measure pass faster as the content is already composed.
@@ -601,18 +377,8 @@
     // this map contains active slotIds (without precomposed or reusable nodes)
     private val slotIdToNode = mutableMapOf<Any?, LayoutNode>()
     private val scope = Scope()
-    private val intermediateMeasureScope = IntermediateMeasureScopeImpl()
+    private val postLookaheadMeasureScope = PostLookaheadMeasureScopeImpl()
 
-    /**
-     * This is the intermediateMeasurePolicy that developers set in [SubcomposeLayout]. It defaults
-     * to invoking [SubcomposeIntermediateMeasureScope.lookaheadMeasurePolicy].
-     *
-     * Note: This intermediate measure policy is only invoked when in a [LookaheadScope].
-     */
-    internal var intermediateMeasurePolicy:
-        (SubcomposeIntermediateMeasureScope.(Constraints) -> MeasureResult) = {
-        lookaheadMeasurePolicy(it)
-    }
     private val precomposeMap = mutableMapOf<Any?, LayoutNode>()
     private val reusableSlotIdsSet = SubcomposeSlotReusePolicy.SlotIdsSet()
 
@@ -840,7 +606,6 @@
     fun createMeasurePolicy(
         block: SubcomposeMeasureScope.(Constraints) -> MeasureResult
     ): MeasurePolicy {
-        intermediateMeasureScope.lookaheadMeasurePolicy = block
         return object : LayoutNode.NoIntrinsicsMeasurePolicy(error = NoIntrinsicsMessage) {
             override fun MeasureScope.measure(
                 measurables: List<Measurable>,
@@ -849,17 +614,14 @@
                 scope.layoutDirection = layoutDirection
                 scope.density = density
                 scope.fontScale = fontScale
-                val isIntermediate =
-                    (root.layoutState == LayoutState.Measuring ||
-                        root.layoutState == LayoutState.LayingOut) && root.lookaheadRoot != null
-                if (isIntermediate) {
-                    return intermediateMeasurePolicy.invoke(intermediateMeasureScope, constraints)
+                if (!isLookingAhead && root.lookaheadRoot != null) {
+                    return with(postLookaheadMeasureScope) {
+                        block(constraints)
+                    }
                 } else {
                     currentIndex = 0
-                    intermediateMeasureScope.lookaheadConstraints = constraints
                     val result = scope.block(constraints)
                     val indexAfterMeasure = currentIndex
-                    intermediateMeasureScope.lookaheadSize = IntSize(result.width, result.height)
                     return object : MeasureResult {
                         override val width: Int
                             get() = result.width
@@ -1008,19 +770,15 @@
             this@LayoutNodeSubcompositionsState.subcompose(slotId, content)
     }
 
-    private inner class IntermediateMeasureScopeImpl :
-        SubcomposeIntermediateMeasureScope, MeasureScope by scope {
-        override fun measurablesForSlot(slotId: Any?): List<Measurable> =
-            slotIdToNode[slotId]?.childMeasurables ?: emptyList()
-
+    private inner class PostLookaheadMeasureScopeImpl :
+        SubcomposeMeasureScope, MeasureScope by scope {
         /**
-         * This is the size returned in the MeasureResult in the measure policy from the lookahead
-         * pass.
+         * This function retrieves [Measurable]s 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].
          */
-        override var lookaheadSize: IntSize = IntSize.Zero
-        override lateinit var lookaheadMeasurePolicy:
-            SubcomposeMeasureScope.(Constraints) -> MeasureResult
-        override var lookaheadConstraints: Constraints = Constraints()
+        override fun subcompose(slotId: Any?, content: @Composable () -> Unit): List<Measurable> =
+            slotIdToNode[slotId]?.childMeasurables ?: emptyList()
     }
 }