DeferredAnimatedVisibility

Functions summary

Unit
@ExperimentalDeferredTransitionApi
@Composable
<T : Any?> DeferredTransition<T>.DeferredAnimatedVisibility(
    visible: (T) -> Boolean,
    modifier: Modifier,
    enter: EnterTransition,
    exit: ExitTransition,
    mutableTransform: MutableTransform?,
    content: @Composable AnimatedVisibilityScope.() -> Unit
)

AnimatedVisibility can be used to animate the appearance and disappearance of its content as the Transition state changes.

Cmn

Functions

DeferredTransition.DeferredAnimatedVisibility

@ExperimentalDeferredTransitionApi
@Composable
fun <T : Any?> DeferredTransition<T>.DeferredAnimatedVisibility(
    visible: (T) -> Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    mutableTransform: MutableTransform? = null,
    content: @Composable AnimatedVisibilityScope.() -> Unit
): Unit

AnimatedVisibility can be used to animate the appearance and disappearance of its content as the Transition state changes.

visible defines whether the content should be visible based on transition state T.

modifier modifier for the Layout created to contain the content

enter EnterTransition(s) used for the appearing animation, fading in while expanding vertically by default

exit ExitTransition(s) used for the disappearing animation, fading out while shrinking vertically by default

mutableTransform A block to control the visual transformations during the deferred phase (e.g., for predictive back gestures) before the main transition begins. This is only active if the Transition was created using rememberTransition with DeferredTransitionState. By default, this is null, meaning no manual transformations are applied. This phase starts when DeferredTransitionState.defer is called and ends when DeferredTransitionState.animateTo is called to start the automatic transition. During this phase, you can manually manipulate the content's transformations (like TransformScope.alpha and TransformScope.scale). These transformations are applied on top of the transition's initial state. Once the transition starts, the manually applied transformations are seamlessly handed off to the configured enter and exit transitions. For exiting content, a "sustain unless specified" policy is applied: if an exit transition (e.g. fadeOut) is specified, the hand-off will animate towards the target value of that transition. However, if no exit transition is specified for a given property (e.g. slideOut is missing), that property will sustain its last manual value until the entire transition completes. While in the deferred phase, entering content remains in the EnterExitState.PreEnter state, and exiting content remains in the EnterExitState.Visible state.

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.DeferredAnimatedVisibility
import androidx.compose.animation.MutableTransform
import androidx.compose.animation.core.DeferredTransitionState
import androidx.compose.animation.core.rememberTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp

// In a real app, these states would be driven by a gesture handler like PredictiveBackHandler
var visible by remember { mutableStateOf(true) }
var isBackGestureInProgress by remember { mutableStateOf(false) }
var swipeOffset by remember { mutableStateOf(IntOffset.Zero) }

val transitionState = remember { DeferredTransitionState(visible) }
val transition = rememberTransition(transitionState)
LaunchedEffect(isBackGestureInProgress, visible) {
    if (isBackGestureInProgress) {
        transitionState.defer(visible)
    } else {
        transitionState.animateTo(visible)
    }
}

transition.DeferredAnimatedVisibility(
    visible = { it },
    mutableTransform =
        MutableTransform { fullSize ->
            if (isBackGestureInProgress) {
                val progressX = (swipeOffset.x.toFloat() / fullSize.width).coerceIn(0f, 1f)
                // Shrink the content down to 80% as the user swipes
                scale = 1f - (progressX * 0.2f)
                // Slide the content along the swipe
                offset = swipeOffset
            }
        },
) {
    Box(Modifier.size(200.dp).background(Color.Red))
}

content Content to appear or disappear based on the visibility derived from the Transition.targetState and the provided visible lambda

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun ItemMainContent() {
    Row(Modifier.height(100.dp).fillMaxWidth(), Arrangement.SpaceEvenly) {
        Box(
            Modifier.size(60.dp)
                .align(Alignment.CenterVertically)
                .background(Color(0xffcdb7f6), CircleShape)
        )
        Column(Modifier.align(Alignment.CenterVertically)) {
            Box(Modifier.height(30.dp).width(300.dp).padding(5.dp).background(Color.LightGray))
            Box(Modifier.height(30.dp).width(300.dp).padding(5.dp).background(Color.LightGray))
        }
    }
}

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun SelectableItem() {
    // This sample animates a number of properties, including AnimatedVisibility, as a part of
    // the Transition going between selected and unselected.
    Box(Modifier.padding(15.dp)) {
        var selected by remember { mutableStateOf(false) }
        // Creates a transition to animate visual changes when `selected` is changed.
        val selectionTransition = updateTransition(selected)
        // Animates the border color as a part of the transition
        val borderColor by
            selectionTransition.animateColor { isSelected ->
                if (isSelected) Color(0xff03a9f4) else Color.White
            }
        // Animates the background color when selected state changes
        val contentBackground by
            selectionTransition.animateColor { isSelected ->
                if (isSelected) Color(0xffdbf0fe) else Color.White
            }
        // Animates elevation as a part of the transition
        val elevation by
            selectionTransition.animateDp { isSelected -> if (isSelected) 10.dp else 2.dp }
        Surface(
            shape = RoundedCornerShape(10.dp),
            border = BorderStroke(2.dp, borderColor),
            modifier = Modifier.clickable { selected = !selected },
            color = contentBackground,
            elevation = elevation,
        ) {
            Column(Modifier.fillMaxWidth()) {
                ItemMainContent()
                // Creates an AnimatedVisibility as a part of the transition, so that when
                // selected it's visible. This will hoist all the animations that are internal
                // to AnimatedVisibility (i.e. fade, slide, etc) to the transition. As a result,
                // `selectionTransition` will not finish until all the animations in
                // AnimatedVisibility as well as animations added directly to it have finished.
                selectionTransition.AnimatedVisibility(
                    visible = { it },
                    enter = expandVertically(),
                    exit = shrinkVertically(),
                ) {
                    Box(Modifier.fillMaxWidth().padding(10.dp)) {
                        Text(
                            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed" +
                                " eiusmod tempor incididunt labore et dolore magna aliqua. " +
                                "Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
                                "laboris nisi ut aliquip ex ea commodo consequat. Duis aute " +
                                "irure dolor."
                        )
                    }
                }
            }
        }
    }
}