LookaheadScope

Known direct subclasses
ExtendedPaneScaffoldScope

Extended scope for pane scaffolds.

SharedTransitionScope

SharedTransitionScope provides a coordinator space in which shared elements/ shared bounds (when matched) will transform their bounds from one to another.

Known indirect subclasses
ExtendedPaneScaffoldPaneScope

Extended scope for the panes of pane scaffolds.

ThreePaneScaffoldPaneScope

Scope for the panes of ThreePaneScaffold.

ThreePaneScaffoldScope

Scope for the panes of ThreePaneScaffold.


LookaheadScope provides a receiver scope for all (direct and indirect) child layouts in LookaheadScope. This receiver scope allows access to lookaheadScopeCoordinates from any child's Placeable.PlacementScope. It also allows any child to convert LayoutCoordinates (which can be retrieved in Placeable.PlacementScope) to LayoutCoordinates in lookahead coordinate space using toLookaheadCoordinates.

import androidx.compose.animation.core.AnimationVector2D import androidx.compose.animation.core.DeferredTargetAnimation import androidx.compose.animation.core.VectorConverter import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ApproachLayoutModifierNode import androidx.compose.ui.layout.ApproachMeasureScope import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.Placeable import androidx.compose.ui.layout.layout import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round /**  * Creates a custom implementation of ApproachLayoutModifierNode to approach the placement of  * the layout using an animation.  */ class AnimatedPlacementModifierNode(var lookaheadScope: LookaheadScope) :  ApproachLayoutModifierNode, Modifier.Node() {  // Creates an offset animation, the target of which will be known during placement.  val offsetAnimation: DeferredTargetAnimation<IntOffset, AnimationVector2D> =  DeferredTargetAnimation(IntOffset.VectorConverter)  override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {  // Since we only animate the placement here, we can consider measurement approach  // complete.  return false  }  // Returns true when the offset animation is in progress, false otherwise.  override fun Placeable.PlacementScope.isPlacementApproachInProgress(  lookaheadCoordinates: LayoutCoordinates  ): Boolean {  val target =  with(lookaheadScope) {  lookaheadScopeCoordinates.localLookaheadPositionOf(lookaheadCoordinates).round()  }  offsetAnimation.updateTarget(target, coroutineScope)  return !offsetAnimation.isIdle  }  override fun ApproachMeasureScope.approachMeasure(  measurable: Measurable,  constraints: Constraints,  ): MeasureResult {  val placeable = measurable.measure(constraints)  return layout(placeable.width, placeable.height) {  val coordinates = coordinates  if (coordinates != null) {  // Calculates the target offset within the lookaheadScope  val target =  with(lookaheadScope) {  lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates).round()  }  // Uses the target offset to start an offset animation  val animatedOffset = offsetAnimation.updateTarget(target, coroutineScope)  // Calculates the *current* offset within the given LookaheadScope  val placementOffset =  with(lookaheadScope) {  lookaheadScopeCoordinates  .localPositionOf(coordinates, Offset.Zero)  .round()  }  // Calculates the delta between animated position in scope and current  // position in scope, and places the child at the delta offset. This puts  // the child layout at the animated position.  val (x, y) = animatedOffset - placementOffset  placeable.place(x, y)  } else {  placeable.place(0, 0)  }  }  } } // Creates a custom node element for the AnimatedPlacementModifierNode above. data class AnimatePlacementNodeElement(val lookaheadScope: LookaheadScope) :  ModifierNodeElement<AnimatedPlacementModifierNode>() {  override fun update(node: AnimatedPlacementModifierNode) {  node.lookaheadScope = lookaheadScope  }  override fun create(): AnimatedPlacementModifierNode {  return AnimatedPlacementModifierNode(lookaheadScope)  } } val colors = listOf(Color(0xffff6f69), Color(0xffffcc5c), Color(0xff264653), Color(0xff2a9d84)) var isInColumn by remember { mutableStateOf(true) } LookaheadScope {  // Creates movable content containing 4 boxes. They will be put either in a [Row] or in a  // [Column] depending on the state  val items = remember {  movableContentOf {  colors.forEach { color ->  Box(  Modifier.padding(15.dp)  .size(100.dp, 80.dp)  .then(AnimatePlacementNodeElement(this))  .background(color, RoundedCornerShape(20))  )  }  }  }  Box(modifier = Modifier.fillMaxSize().clickable { isInColumn = !isInColumn }) {  // As the items get moved between Column and Row, their positions in LookaheadScope  // will change. The `animatePlacementInScope` modifier created above will  // observe that final position change via `localLookaheadPositionOf`, and create  // a position animation.  if (isInColumn) {  Column(Modifier.fillMaxSize()) { items() }  } else {  Row { items() }  }  } }

Summary

Public functions

open Offset
LayoutCoordinates.localLookaheadPositionOf(
    sourceCoordinates: LayoutCoordinates,
    relativeToSource: Offset,
    includeMotionFrameOfReference: Boolean
)

Converts relativeToSource in sourceCoordinates's lookahead coordinate space into local lookahead coordinates.

Cmn
LayoutCoordinates

Converts a LayoutCoordinates into a LayoutCoordinates in the Lookahead coordinate space.

Cmn

Extension functions

LayoutCoordinates

Obtains the LayoutCoordinates for the given LookaheadScope using a LayoutCoordinates within the LookaheadScope.

Cmn

Public functions

localLookaheadPositionOf

open fun LayoutCoordinates.localLookaheadPositionOf(
    sourceCoordinates: LayoutCoordinates,
    relativeToSource: Offset = Offset.Zero,
    includeMotionFrameOfReference: Boolean = true
): Offset

Converts relativeToSource in sourceCoordinates's lookahead coordinate space into local lookahead coordinates. This is a convenient method for 1) converting both this coordinates and sourceCoordinates into lookahead space coordinates using toLookaheadCoordinates, and 2) invoking LayoutCoordinates.localPositionOf with the converted coordinates.

For layouts where LayoutCoordinates.introducesMotionFrameOfReference returns true (placed under Placeable.PlacementScope.withMotionFrameOfReferencePlacement) you may pass includeMotionFrameOfReference as false to get their position while excluding the additional Offset.

toLookaheadCoordinates

fun LayoutCoordinates.toLookaheadCoordinates(): LayoutCoordinates

Converts a LayoutCoordinates into a LayoutCoordinates in the Lookahead coordinate space. This can be used for layouts within LookaheadScope.

Public properties

lookaheadScopeCoordinates

val Placeable.PlacementScope.lookaheadScopeCoordinatesLayoutCoordinates

Returns the LayoutCoordinates of the LookaheadScope. This is only accessible from Placeable.PlacementScope (i.e. during placement time).

Note: The returned coordinates is not coordinates in the lookahead coordinate space. If the lookahead coordinates of the lookaheadScope is needed, suggest converting the returned coordinates using toLookaheadCoordinates.

Extension functions

lookaheadScopeCoordinates

fun LookaheadScope.lookaheadScopeCoordinates(
    sourceCoordinates: LayoutCoordinates
): LayoutCoordinates

Obtains the LayoutCoordinates for the given LookaheadScope using a LayoutCoordinates within the LookaheadScope.

Important: This must be an actual LayoutCoordinates instance from the PlacementScope or Modifier APIs. The Layout that associates with the coordinates needs to be within the subtree of the LookaheadScope. Using a custom LayoutCoordinates implementation will result in an IllegalArgumentException.

Parameters
sourceCoordinates: LayoutCoordinates

A LayoutCoordinates within the subtree of the given LookaheadScope.