OnPlacedModifier


A modifier whose onPlaced is called 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.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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.composed
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.unit.round

@OptIn(ExperimentalComposeUiApi::class)
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
    }
}

@OptIn(ExperimentalComposeUiApi::class)
@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)
        )
    }
}

Summary

Public functions

Unit

onPlaced is called after parent LayoutModifier and parent layout gets placed and before any child LayoutModifier is placed.

Cmn

Inherited functions

From androidx.compose.ui.Modifier
open infix Modifier
then(other: Modifier)

Concatenates this modifier with another.

Cmn
From androidx.compose.ui.Modifier.Element
open Boolean
all(predicate: (Modifier.Element) -> Boolean)

Returns true if predicate returns true for all Elements in this Modifier or if this Modifier contains no Elements.

Cmn
open Boolean
any(predicate: (Modifier.Element) -> Boolean)

Returns true if predicate returns true for any Element in this Modifier.

Cmn
open R
<R : Any?> foldIn(initial: R, operation: (Modifier.Element, R) -> R)

Accumulates a value starting with initial and applying operation to the current value and each element from outside in.

Cmn
open R
<R : Any?> foldOut(initial: R, operation: (Modifier.Element, R) -> R)

Accumulates a value starting with initial and applying operation to the current value and each element from inside out.

Cmn

Public functions

onPlaced

fun onPlaced(coordinates: LayoutCoordinates): Unit

onPlaced is called after parent LayoutModifier and parent layout gets placed and before any child LayoutModifier is placed.

coordinates provides LayoutCoordinates of the OnPlacedModifier. Placement in both parent LayoutModifier and parent layout can be calculated using the LayoutCoordinates.