androidx.compose.ui.layout

Interfaces

ApproachIntrinsicMeasureScope

The receiver scope of a layout's intrinsic approach measurements lambdas.

Cmn
ApproachLayoutModifierNode

ApproachLayoutModifierNode is designed to support gradually approaching the destination layout calculated in the lookahead pass.

Cmn
ApproachMeasureScope

ApproachMeasureScope provides access to lookahead results to allow ApproachLayoutModifierNode to leverage lookahead results to define how measurements and placements approach their destination.

Cmn
BeyondBoundsLayout

Layout extra items in the specified direction.

Cmn
BeyondBoundsLayout.BeyondBoundsScope

The scope used in BeyondBoundsLayout.layout.

Cmn
ContentScale

Represents a rule to apply to scale a source rectangle to be inscribed into a destination

Cmn
GraphicLayerInfo

The info about the graphics layers used by tooling.

android
IntrinsicMeasurable

A part of the composition that can be measured.

Cmn
IntrinsicMeasureScope

The receiver scope of a layout's intrinsic measurements lambdas.

Cmn
LayoutCoordinates

A holder of the measured bounds for the Layout.

Cmn
LayoutIdParentData

Can be implemented by values used as parent data to make them usable as tags.

Cmn
LayoutInfo

The public information about the layouts used internally as nodes in the Compose UI hierarchy.

Cmn
LayoutModifier

A Modifier.Element that changes how its wrapped content is measured and laid out.

Cmn
LookaheadScope

LookaheadScope provides a receiver scope for all (direct and indirect) child layouts in LookaheadScope.

Cmn
Measurable

A part of the composition that can be measured.

Cmn
MeasurePolicy

Defines the measure and layout behavior of a Layout.

Cmn
MeasureResult

Interface holding the size and alignment lines of the measured layout, as well as the children positioning logic.

Cmn
MeasureScope

The receiver scope of a layout's measure lambda.

Cmn
Measured

A Measured corresponds to a layout that has been measured by its parent layout.

Cmn
MultiContentMeasurePolicy

Defines the measure and layout behavior of a Layout overload which accepts a list of multiple composable content lambdas.

Cmn
OnGloballyPositionedModifier

A modifier whose onGloballyPositioned is called with the final LayoutCoordinates of the Layout when the global position of the content may have changed.

Cmn
OnPlacedModifier

A modifier whose onPlaced is called after the parent LayoutModifier and parent layout has been placed and before child LayoutModifier is placed.

Cmn
OnRemeasuredModifier

A modifier whose onRemeasured is called when the layout content is remeasured.

Cmn
ParentDataModifier

A Modifier that provides data to the parent Layout.

Cmn
PinnableContainer

Represents a container which can be pinned when the content of this container is important.

Cmn
PinnableContainer.PinnedHandle

This is an object returned by pin which allows to release the pinning.

Cmn
Remeasurement

This object is associated with a layout node and allows to execute some extra measure/layout actions which are needed for some complex layouts.

Cmn
RemeasurementModifier

A Modifier.Element that provides a Remeasurement object associated with the layout node the modifier is applied to.

Cmn
RulerScope

A scope used in MeasureScope.layout for the rulers parameter to allow a layout to define Ruler values for children.

Cmn
SubcomposeLayoutState.PrecomposedSlotHandle

Instance of this interface is returned by precompose function.

Cmn
SubcomposeMeasureScope

The receiver scope of a SubcomposeLayout's measure lambda which adds ability to dynamically subcompose a content during the measuring on top of the features provided by MeasureScope.

Cmn
SubcomposeSlotReusePolicy

This policy allows SubcomposeLayout to retain some of slots which we were used but not used anymore instead of disposing them.

Cmn

Classes

AlignmentLine

Defines an offset line that can be used by parent layouts to align and position their children.

Cmn
BeyondBoundsLayout.LayoutDirection

The direction (from the visible bounds) that a BeyondBoundsLayout is requesting more items to be laid.

Cmn
FixedScale

ContentScale implementation that always scales the dimension by the provided fixed floating point value

Cmn
HorizontalAlignmentLine

A horizontal AlignmentLine.

Cmn
HorizontalRuler

A horizontal Ruler.

Cmn
ModifierInfo

Used by tooling to examine the modifiers on a LayoutInfo.

Cmn
Placeable

A Placeable corresponds to a child layout that can be positioned by its parent layout.

Cmn
Placeable.PlacementScope

Receiver scope that permits explicit placement of a Placeable.

Cmn
Ruler

A line that can be used to align layout children inside a Placeable.PlacementScope.

Cmn
ScaleFactor

Holds 2 dimensional scaling factors for horizontal and vertical axes

Cmn
SubcomposeLayoutState

State used by SubcomposeLayout.

Cmn
SubcomposeSlotReusePolicy.SlotIdsSet

Set containing slot ids currently available to reuse.

Cmn
VerticalAlignmentLine

A vertical AlignmentLine.

Cmn
VerticalRuler

A vertical Ruler.

Cmn

Annotations

Top-level functions summary

inline Unit
@Composable
@UiComposable
Layout(modifier: Modifier, measurePolicy: MeasurePolicy)

Layout is the main core component for layout for "leaf" nodes.

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

Layout is the main core component for layout.

Cmn
inline Unit
@UiComposable
@Composable
Layout(
    contents: List<@Composable @UiComposable () -> Unit>,
    modifier: Modifier,
    measurePolicy: MultiContentMeasurePolicy
)

Layout is the main core component for layout.

Cmn
Unit

LookaheadScope creates a scope in which all layouts will first determine their destination layout through a lookahead pass, followed by an approach pass to run the measurement and placement approach defined in approachLayout or ApproachLayoutModifierNode, in order to gradually reach the destination.

Cmn
Unit
@Composable
@UiComposable
MultiMeasureLayout(
    modifier: Modifier,
    content: @Composable @UiComposable () -> Unit,
    measurePolicy: MeasurePolicy
)

This function is deprecated. This API is unsafe for UI performance at scale - using it incorrectly will lead to exponential performance issues.

Cmn
inline ScaleFactor
ScaleFactor(scaleX: Float, scaleY: Float)

Constructs a ScaleFactor from the given x and y scale values

Cmn
Unit
@Composable
SubcomposeLayout(
    modifier: Modifier,
    measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
)

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.

Cmn
Unit

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.

Cmn
SubcomposeSlotReusePolicy
SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse: Int)

Creates SubcomposeSlotReusePolicy which retains the fixed amount of slots.

Cmn
ScaleFactor
lerp(start: ScaleFactor, stop: ScaleFactor, fraction: Float)

Linearly interpolate between two ScaleFactor parameters

Cmn

Extension functions summary

Modifier
Modifier.approachLayout(
    isMeasurementApproachInProgress: (lookaheadSize: IntSize) -> Boolean,
    isPlacementApproachInProgress: Placeable.PlacementScope.(lookaheadCoordinates: LayoutCoordinates) -> Boolean,
    approachMeasure: ApproachMeasureScope.(measurable: Measurable, constraints: Constraints) -> MeasureResult
)

Creates an approach layout intended to help gradually approach the destination layout calculated in the lookahead pass.

Cmn
Rect

Returns the bounding box of the child in the parent's content area, including any clipping done with respect to the parent.

Cmn
Rect

The boundaries of this layout inside the root composable.

Cmn
Rect

The boundaries of this layout relative to the window's origin.

Cmn
operator Size
Size.div(scaleFactor: ScaleFactor)

Division operator with Size

Cmn
LayoutCoordinates

Walks up the LayoutCoordinates hierarchy to find the LayoutCoordinates whose LayoutCoordinates.parentCoordinates is null and returns it.

Cmn
Modifier

Creates a LayoutModifier that allows changing how the wrapped element is measured and laid out.

Cmn
Modifier
Modifier.layoutId(layoutId: Any)

Tag the element with layoutId to identify the element within its parent.

Cmn
Modifier
Modifier.onGloballyPositioned(
    onGloballyPositioned: (LayoutCoordinates) -> Unit
)

Invoke onGloballyPositioned with the LayoutCoordinates of the element when the global position of the content may have changed.

Cmn
Modifier

Invoke onPlaced after the parent LayoutModifier and parent layout has been placed and before child LayoutModifier is placed.

Cmn
Modifier
Modifier.onRectChanged(
    throttleMs: Int,
    debounceMs: Int,
    callback: (RectInfo) -> Unit
)

Invokes callback with the position of this layout node relative to the coordinate system of the root of the composition, as well as in screen coordinates and window coordinates.

Cmn
Modifier
Modifier.onSizeChanged(onSizeChanged: (IntSize) -> Unit)

Invoked with the size of the modified Compose UI element when the element is first measured or when the size of the element changes.

Cmn
Offset

Returns the position of the top-left in the parent's content area or (0, 0) for the root.

Cmn
Offset

The position of this layout inside the root composable.

Cmn
Offset

The position of this layout relative to the window.

Cmn
Offset

The position of this layout on the device's screen.

Cmn
DisposableHandle
DelegatableNode.registerOnRectChanged(
    throttleMs: Int,
    debounceMs: Int,
    callback: (RectInfo) -> Unit
)

Registers a callback to be executed with the position of this modifier node relative to the coordinate system of the root of the composition, as well as in screen coordinates and window coordinates.

Cmn
inline ScaleFactor

If this ScaleFactor then this is returned, otherwise block is executed and its result is returned.

Cmn
operator Size
Size.times(scaleFactor: ScaleFactor)

Multiplication operator with Size.

Cmn
operator Size

Multiplication operator with Size with reverse parameter types to maintain commutative properties of multiplication

Cmn

Top-level properties summary

HorizontalAlignmentLine

AlignmentLine defined by the baseline of a first line of a androidx.compose.foundation.text.BasicText

Cmn
HorizontalAlignmentLine

AlignmentLine defined by the baseline of the last line of a androidx.compose.foundation.text.BasicText

Cmn
ProvidableCompositionLocal<PinnableContainer?>

Use this composition local to get the PinnableContainer handling the current subhierarchy.

Cmn
ProvidableModifierLocal<BeyondBoundsLayout?>

A modifier local that provides access to a BeyondBoundsLayout that a child can use to ask a parent to layout more items that are beyond its visible bounds.

Cmn

Extension properties summary

Boolean

false when this is ScaleFactor.Unspecified.

Cmn
Boolean

true when this is ScaleFactor.Unspecified.

Cmn
Any?

Retrieves the tag associated to a composable with the Modifier.layoutId modifier.

Cmn
View?

Return the owner as a View from the associated LayoutNode.

android

Top-level functions

Layout

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

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:

import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Constraints

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

Example usage with custom intrinsic measurements:

import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.IntrinsicMeasureScope
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Constraints

// 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)
Parameters
modifier: Modifier = Modifier

Modifiers to be applied to the layout.

measurePolicy: MeasurePolicy

The policy defining the measurement and positioning of the layout.

Layout

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

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:

import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Constraints

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

Example usage with custom intrinsic measurements:

import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.IntrinsicMeasureScope
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Constraints

// 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)
Parameters
content: @Composable @UiComposable () -> Unit

The children composable to be laid out.

modifier: Modifier = Modifier

Modifiers to be applied to the layout.

measurePolicy: MeasurePolicy

The policy defining the measurement and positioning of the layout.

Layout

@UiComposable
@Composable
inline fun Layout(
    contents: List<@Composable @UiComposable () -> Unit>,
    modifier: Modifier = Modifier,
    measurePolicy: MultiContentMeasurePolicy
): Unit

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.

Example usage:

import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.layout

// 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
        }
    }
}
Parameters
contents: List<@Composable @UiComposable () -> Unit>

The list of children composable contents to be laid out.

modifier: Modifier = Modifier

Modifiers to be applied to the layout.

measurePolicy: MultiContentMeasurePolicy

The policy defining the measurement and positioning of the layout.

See also
Layout

for a simpler use case when you have only one content lambda.

LookaheadScope

@UiComposable
@Composable
fun LookaheadScope(content: @Composable @UiComposable LookaheadScope.() -> Unit): Unit

LookaheadScope creates a scope in which all layouts will first determine their destination layout through a lookahead pass, followed by an approach pass to run the measurement and placement approach defined in approachLayout or ApproachLayoutModifierNode, in order to gradually reach the destination.

Note: LookaheadScope does not introduce a new Layout to the content passed in. All the Layouts in the content will have the same parent as they would without LookaheadScope.

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() }
        }
    }
}
Parameters
content: @Composable @UiComposable LookaheadScope.() -> Unit

The child composable to be laid out.

MultiMeasureLayout

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

ScaleFactor

inline fun ScaleFactor(scaleX: Float, scaleY: Float): ScaleFactor

Constructs a ScaleFactor from the given x and y scale values

SubcomposeLayout

@Composable
fun SubcomposeLayout(
    modifier: Modifier = Modifier,
    measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
): Unit

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

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

// enum class SlotsEnum { Main, Dependent }
SubcomposeLayout { constraints ->
    val mainPlaceables = subcompose(SlotsEnum.Main, mainContent).map { it.measure(constraints) }
    val maxSize =
        mainPlaceables.fold(IntSize.Zero) { currentMax, placeable ->
            IntSize(
                width = maxOf(currentMax.width, placeable.width),
                height = maxOf(currentMax.height, placeable.height)
            )
        }
    layout(maxSize.width, maxSize.height) {
        mainPlaceables.forEach { it.placeRelative(0, 0) }
        subcompose(SlotsEnum.Dependent) { dependentContent(maxSize) }
            .forEach { it.measure(constraints).placeRelative(0, 0) }
    }
}
Parameters
modifier: Modifier = Modifier

Modifier to apply for the layout.

measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult

Measure policy which provides ability to subcompose during the measuring.

SubcomposeLayout

@Composable
@UiComposable
fun SubcomposeLayout(
    state: SubcomposeLayoutState,
    modifier: Modifier = Modifier,
    measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult
): Unit

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

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

// enum class SlotsEnum { Main, Dependent }
SubcomposeLayout { constraints ->
    val mainPlaceables = subcompose(SlotsEnum.Main, mainContent).map { it.measure(constraints) }
    val maxSize =
        mainPlaceables.fold(IntSize.Zero) { currentMax, placeable ->
            IntSize(
                width = maxOf(currentMax.width, placeable.width),
                height = maxOf(currentMax.height, placeable.height)
            )
        }
    layout(maxSize.width, maxSize.height) {
        mainPlaceables.forEach { it.placeRelative(0, 0) }
        subcompose(SlotsEnum.Dependent) { dependentContent(maxSize) }
            .forEach { it.measure(constraints).placeRelative(0, 0) }
    }
}
Parameters
state: SubcomposeLayoutState

the state object to be used by the layout.

modifier: Modifier = Modifier

Modifier to apply for the layout.

measurePolicy: SubcomposeMeasureScope.(Constraints) -> MeasureResult

Measure policy which provides ability to subcompose during the measuring.

SubcomposeSlotReusePolicy

fun SubcomposeSlotReusePolicy(maxSlotsToRetainForReuse: Int): SubcomposeSlotReusePolicy

Creates SubcomposeSlotReusePolicy which retains the fixed amount of slots.

Parameters
maxSlotsToRetainForReuse: Int

the SubcomposeLayout will retain up to this amount of slots.

lerp

fun lerp(start: ScaleFactor, stop: ScaleFactor, fraction: Float): ScaleFactor

Linearly interpolate between two ScaleFactor parameters

The fraction argument represents position on the timeline, with 0.0 meaning that the interpolation has not started, returning start (or something equivalent to start), 1.0 meaning that the interpolation has finished, returning stop (or something equivalent to stop), and values in between meaning that the interpolation is at the relevant point on the timeline between start and stop. The interpolation can be extrapolated beyond 0.0 and 1.0, so negative values and values greater than 1.0 are valid (and can easily be generated by curves).

Values for fraction are usually obtained from an Animation, such as an AnimationController.

Extension functions

approachLayout

fun Modifier.approachLayout(
    isMeasurementApproachInProgress: (lookaheadSize: IntSize) -> Boolean,
    isPlacementApproachInProgress: Placeable.PlacementScope.(lookaheadCoordinates: LayoutCoordinates) -> Boolean = defaultPlacementApproachInProgress,
    approachMeasure: ApproachMeasureScope.(measurable: Measurable, constraints: Constraints) -> MeasureResult
): Modifier

Creates an approach layout intended to help gradually approach the destination layout calculated in the lookahead pass. This can be particularly helpful when the destination layout is anticipated to change drastically and would consequently result in visual disruptions.

In order to create a smooth approach, an interpolation (often through animations) can be used in approachMeasure to interpolate the measurement or placement from a previously recorded size and/or position to the destination/target size and/or position. The destination size is available in ApproachMeasureScope as ApproachMeasureScope.lookaheadSize. And the target position can also be acquired in ApproachMeasureScope during placement by using LookaheadScope.localLookaheadPositionOf with the layout's Placeable.PlacementScope.coordinates. The sample code below illustrates how that can be achieved.

isMeasurementApproachInProgress signals whether the measurement is in progress of approaching destination size. It will be queried after the destination has been determined by the lookahead pass, before approachMeasure is invoked. The lookahead size is provided to isMeasurementApproachInProgress for convenience in deciding whether the destination size has been reached.

isMeasurementApproachInProgress indicates whether the position is currently approaching destination defined by the lookahead, hence it's a signal to the system for whether additional approach placements are necessary. isPlacementApproachInProgress will be invoked after the destination position has been determined by lookahead pass, and before the placement phase in approachMeasure.

Once both isMeasurementApproachInProgress and isPlacementApproachInProgress return false, the system may skip approach pass until additional approach passes are necessary as indicated by isMeasurementApproachInProgress and isPlacementApproachInProgress.

IMPORTANT: It is important to be accurate in isPlacementApproachInProgress and isMeasurementApproachInProgress. A prolonged indication of incomplete approach will prevent the system from potentially skipping approach pass when possible.

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.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round

// Creates a custom modifier that animates the constraints and measures child with the
// animated constraints. This modifier is built on top of `Modifier.approachLayout` to approach
// th destination size determined by the lookahead pass. A resize animation will be kicked off
// whenever the lookahead size changes, to animate children from current size to destination
// size. Fixed constraints created based on the animation value will be used to measure
// child, so the child layout gradually changes its animated constraints until the approach
// completes.
fun Modifier.animateConstraints(
    sizeAnimation: DeferredTargetAnimation<IntSize, AnimationVector2D>,
    coroutineScope: CoroutineScope
) =
    this.approachLayout(
        isMeasurementApproachInProgress = { lookaheadSize ->
            // Update the target of the size animation.
            sizeAnimation.updateTarget(lookaheadSize, coroutineScope)
            // Return true if the size animation has pending target change or hasn't finished
            // running.
            !sizeAnimation.isIdle
        }
    ) { measurable, _ ->
        // In the measurement approach, the goal is to gradually reach the destination size
        // (i.e. lookahead size). To achieve that, we use an animation to track the current
        // size, and animate to the destination size whenever it changes. Once the animation
        // finishes, the approach is complete.

        // First, update the target of the animation, and read the current animated size.
        val (width, height) = sizeAnimation.updateTarget(lookaheadSize, coroutineScope)
        // Then create fixed size 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) }
    }

var fullWidth by remember { mutableStateOf(false) }

// Creates a size animation with a target unknown at the time of instantiation.
val sizeAnimation = remember { DeferredTargetAnimation(IntSize.VectorConverter) }
val coroutineScope = rememberCoroutineScope()
Row(
    (if (fullWidth) Modifier.fillMaxWidth() else Modifier.width(100.dp))
        .height(200.dp)
        // Use the custom modifier created above to animate the constraints passed
        // to the child, and therefore resize children in an animation.
        .animateConstraints(sizeAnimation, coroutineScope)
        .clickable { fullWidth = !fullWidth }
) {
    Box(
        Modifier.weight(1f).fillMaxHeight().background(Color(0xffff6f69)),
    )
    Box(Modifier.weight(2f).fillMaxHeight().background(Color(0xffffcc5c)))
}

boundsInParent

fun LayoutCoordinates.boundsInParent(): Rect

Returns the bounding box of the child in the parent's content area, including any clipping done with respect to the parent. For the root, the bounds is positioned at (0, 0) and sized to the size of the root.

boundsInRoot

fun LayoutCoordinates.boundsInRoot(): Rect

The boundaries of this layout inside the root composable.

boundsInWindow

fun LayoutCoordinates.boundsInWindow(): Rect

The boundaries of this layout relative to the window's origin.

operator fun Size.div(scaleFactor: ScaleFactor): Size

Division operator with Size

Return a new Size with the width and height divided by ScaleFactor.scaleX and ScaleFactor.scaleY respectively

findRootCoordinates

fun LayoutCoordinates.findRootCoordinates(): LayoutCoordinates

Walks up the LayoutCoordinates hierarchy to find the LayoutCoordinates whose LayoutCoordinates.parentCoordinates is null and returns it. If LayoutCoordinates.isAttached, this will have the size of the androidx.compose.ui.platform.ComposeView.

layout

fun Modifier.layout(measure: MeasureScope.(Measurable, Constraints) -> MeasureResult): Modifier

Creates a LayoutModifier that allows changing how the wrapped element is measured and laid out.

This is a convenience API of creating a custom LayoutModifier modifier, without having to create a class or an object that implements the LayoutModifier interface. The intrinsic measurements follow the default logic provided by the LayoutModifier.

Example usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.offset

Box(
    Modifier.background(Color.Gray).layout { measurable, constraints ->
        // an example modifier that adds 50 pixels of vertical padding.
        val padding = 50
        val placeable = measurable.measure(constraints.offset(vertical = -padding))
        layout(placeable.width, placeable.height + padding) {
            placeable.placeRelative(0, padding)
        }
    }
) {
    Box(Modifier.fillMaxSize().background(Color.DarkGray))
}
See also
LayoutModifier

layoutId

fun Modifier.layoutId(layoutId: Any): Modifier

Tag the element with layoutId to identify the element within its parent.

Example usage:

import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.unit.Constraints

Layout({
    // Here the Containers are only needed to apply the modifiers. You could use the
    // modifier on header and footer directly if they are composables accepting modifiers.
    Box(Modifier.layoutId("header")) { header() }
    Box(Modifier.layoutId("footer")) { footer() }
}) { measurables, constraints ->
    val placeables =
        measurables.map { measurable ->
            when (measurable.layoutId) {
                // You should use appropriate constraints. Here we measure fake constraints.
                "header" -> measurable.measure(Constraints.fixed(100, 100))
                "footer" -> measurable.measure(constraints)
                else -> error("Unexpected tag")
            }
        }
    // Size should be derived from children measured sizes on placeables,
    // but this is simplified for the purposes of the example.
    layout(100, 100) { placeables.forEach { it.placeRelative(0, 0) } }
}

onGloballyPositioned

fun Modifier.onGloballyPositioned(
    onGloballyPositioned: (LayoutCoordinates) -> Unit
): Modifier

Invoke onGloballyPositioned with the LayoutCoordinates of the element when the global position of the content may have changed. Note that it will be called after a composition when the coordinates are finalized.

This callback will be invoked at least once when the LayoutCoordinates are available, and every time the element's position changes within the window. However, it is not guaranteed to be invoked every time the position relative to the screen of the modified element changes. For example, the system may move the contents inside a window around without firing a callback. If you are using the LayoutCoordinates to calculate position on the screen, and not just inside the window, you may not receive a callback.

Usage example:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.unit.dp

Column(
    Modifier.onGloballyPositioned { coordinates ->
        // This will be the size of the Column.
        coordinates.size
        // The position of the Column relative to the application window.
        coordinates.positionInWindow()
        // The position of the Column relative to the Compose root.
        coordinates.positionInRoot()
        // These will be the alignment lines provided to the layout (empty here for Column).
        coordinates.providedAlignmentLines
        // This will be a LayoutCoordinates instance corresponding to the parent of Column.
        coordinates.parentLayoutCoordinates
    }
) {
    Box(Modifier.size(20.dp).background(Color.Green))
    Box(Modifier.size(20.dp).background(Color.Blue))
}

onPlaced

fun Modifier.onPlaced(onPlaced: (LayoutCoordinates) -> Unit): Modifier

Invoke onPlaced after the parent LayoutModifier and parent layout has been placed and before child LayoutModifier is placed. This allows child LayoutModifier to adjust its own placement based on where the parent is.

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector2D
import androidx.compose.animation.core.Spring.StiffnessMediumLow
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round

fun Modifier.animatePlacement(): Modifier = composed {
    val scope = rememberCoroutineScope()
    var targetOffset by remember { mutableStateOf(IntOffset.Zero) }
    var animatable by remember {
        mutableStateOf<Animatable<IntOffset, AnimationVector2D>?>(null)
    }
    this.onPlaced {
            // Calculate the position in the parent layout
            targetOffset = it.positionInParent().round()
        }
        .offset {
            // Animate to the new target offset when alignment changes.
            val anim =
                animatable
                    ?: Animatable(targetOffset, IntOffset.VectorConverter).also {
                        animatable = it
                    }
            if (anim.targetValue != targetOffset) {
                scope.launch {
                    anim.animateTo(targetOffset, spring(stiffness = StiffnessMediumLow))
                }
            }
            // Offset the child in the opposite direction to the targetOffset, and slowly catch
            // up to zero offset via an animation to achieve an overall animated movement.
            animatable?.let { it.value - targetOffset } ?: IntOffset.Zero
        }
}

@Composable
fun AnimatedChildAlignment(alignment: Alignment) {
    Box(Modifier.fillMaxSize().padding(4.dp).border(1.dp, Color.Red)) {
        Box(
            modifier =
                Modifier.animatePlacement().align(alignment).size(100.dp).background(Color.Red)
        )
    }
}

onRectChanged

fun Modifier.onRectChanged(
    throttleMs: Int = 0,
    debounceMs: Int = 64,
    callback: (RectInfo) -> Unit
): Modifier

Invokes callback with the position of this layout node relative to the coordinate system of the root of the composition, as well as in screen coordinates and window coordinates. This will be called after layout pass. This API allows for throttling and debouncing parameters in order to moderate the frequency with which the callback gets invoked during high rates of change (e.g. scrolling).

Specifying throttleMs will prevent callback from being executed more than once over that time period. Specifying debounceMs will delay the execution of callback until that amount of time has elapsed without a new position, scheduling the callback to be executed when that amount of time expires.

Specifying 0 for both throttleMs and debounceMs will result in the callback being executed every time the position has changed. Specifying non-zero amounts for both will result in both conditions being met. Specifying a non-zero throttleMs but a zero debounceMs is equivalent to providing the same value for both throttleMs and debounceMs.

Parameters
throttleMs: Int = 0

The duration, in milliseconds, to prevent callback from being executed more than once over that time period.

debounceMs: Int = 64

The duration, in milliseconds, to delay the execution of callback until that amount of time has elapsed without a new position.

callback: (RectInfo) -> Unit

The callback to be executed.

onSizeChanged

fun Modifier.onSizeChanged(onSizeChanged: (IntSize) -> Unit): Modifier

Invoked with the size of the modified Compose UI element when the element is first measured or when the size of the element changes.

There are no guarantees onSizeChanged will not be re-invoked with the same size.

Using the onSizeChanged size value in a MutableState to update layout causes the new size value to be read and the layout to be recomposed in the succeeding frame, resulting in a one frame lag.

You can use onSizeChanged to affect drawing operations. Use Layout or SubcomposeLayout to enable the size of one component to affect the size of another.

Example usage:

import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged

// Use onSizeChanged() for diagnostics. Use Layout or SubcomposeLayout if you want
// to use the size of one component to affect the size of another component.
Text(
    "Hello $name",
    Modifier.onSizeChanged { size -> println("The size of the Text in pixels is $size") }
)

positionInParent

fun LayoutCoordinates.positionInParent(): Offset

Returns the position of the top-left in the parent's content area or (0, 0) for the root.

positionInRoot

fun LayoutCoordinates.positionInRoot(): Offset

The position of this layout inside the root composable.

positionInWindow

fun LayoutCoordinates.positionInWindow(): Offset

The position of this layout relative to the window.

positionOnScreen

fun LayoutCoordinates.positionOnScreen(): Offset

The position of this layout on the device's screen. Returns Offset.Unspecified if the conversion cannot be performed.

registerOnRectChanged

fun DelegatableNode.registerOnRectChanged(
    throttleMs: Int,
    debounceMs: Int,
    callback: (RectInfo) -> Unit
): DisposableHandle

Registers a callback to be executed with the position of this modifier node relative to the coordinate system of the root of the composition, as well as in screen coordinates and window coordinates. This will be called after layout pass. This API allows for throttling and debouncing parameters in order to moderate the frequency with which the callback gets invoked during high rates of change (e.g. scrolling).

Specifying throttleMs will prevent callback from being executed more than once over that time period. Specifying debounceMs will delay the execution of callback until that amount of time has elapsed without a new position.

Specifying 0 for both throttleMs and debounceMs will result in the callback being executed every time the position has changed. Specifying non-zero amounts for both will result in both conditions being met.

Parameters
throttleMs: Int

The duration, in milliseconds, to prevent callback from being executed more than once over that time period.

debounceMs: Int

The duration, in milliseconds, to delay the execution of callback until that amount of time has elapsed without a new position.

callback: (RectInfo) -> Unit

The callback to be executed.

Returns
DisposableHandle

an object which should be used to unregister/dispose this callback

See also
onRectChanged

takeOrElse

inline fun ScaleFactor.takeOrElse(block: () -> ScaleFactor): ScaleFactor

If this ScaleFactor then this is returned, otherwise block is executed and its result is returned.

times

operator fun Size.times(scaleFactor: ScaleFactor): Size

Multiplication operator with Size.

Return a new Size with the width and height multiplied by the ScaleFactor.scaleX and ScaleFactor.scaleY respectively

times

operator fun ScaleFactor.times(size: Size): Size

Multiplication operator with Size with reverse parameter types to maintain commutative properties of multiplication

Return a new Size with the width and height multiplied by the ScaleFactor.scaleX and ScaleFactor.scaleY respectively

Top-level properties

FirstBaseline

val FirstBaselineHorizontalAlignmentLine

AlignmentLine defined by the baseline of a first line of a androidx.compose.foundation.text.BasicText

LastBaseline

val LastBaselineHorizontalAlignmentLine

AlignmentLine defined by the baseline of the last line of a androidx.compose.foundation.text.BasicText

LocalPinnableContainer

val LocalPinnableContainerProvidableCompositionLocal<PinnableContainer?>

Use this composition local to get the PinnableContainer handling the current subhierarchy.

It will be not null, for example, when the current content is composed as an item of lazy list.

ModifierLocalBeyondBoundsLayout

val ModifierLocalBeyondBoundsLayoutProvidableModifierLocal<BeyondBoundsLayout?>

A modifier local that provides access to a BeyondBoundsLayout that a child can use to ask a parent to layout more items that are beyond its visible bounds.

Extension properties

isSpecified

val ScaleFactor.isSpecifiedBoolean

false when this is ScaleFactor.Unspecified.

isUnspecified

val ScaleFactor.isUnspecifiedBoolean

true when this is ScaleFactor.Unspecified.

layoutId

val Measurable.layoutIdAny?

Retrieves the tag associated to a composable with the Modifier.layoutId modifier. For a parent data value to be returned by this property when not using the Modifier.layoutId modifier, the parent data value should implement the LayoutIdParentData interface.

Example usage:

import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.unit.Constraints

Layout({
    // Here the Containers are only needed to apply the modifiers. You could use the
    // modifier on header and footer directly if they are composables accepting modifiers.
    Box(Modifier.layoutId("header")) { header() }
    Box(Modifier.layoutId("footer")) { footer() }
}) { measurables, constraints ->
    val placeables =
        measurables.map { measurable ->
            when (measurable.layoutId) {
                // You should use appropriate constraints. Here we measure fake constraints.
                "header" -> measurable.measure(Constraints.fixed(100, 100))
                "footer" -> measurable.measure(constraints)
                else -> error("Unexpected tag")
            }
        }
    // Size should be derived from children measured sizes on placeables,
    // but this is simplified for the purposes of the example.
    layout(100, 100) { placeables.forEach { it.placeRelative(0, 0) } }
}

view

val LayoutInfo.viewView?

Return the owner as a View from the associated LayoutNode.