Compose incluye elementos componibles y modificadores integrados para controlar casos de uso comunes de animación.
Elementos componibles animados integrados
Compose proporciona varios elementos componibles que animan la aparición, la desaparición y los cambios de diseño del contenido.
Cómo animar la aparición y la desaparición

El elemento componible AnimatedVisibility
anima la aparición y la desaparición de su contenido.
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
De forma predeterminada, el contenido aparece atenuado y expandido, y desaparece desvaneciéndose y achicándose. Para personalizar esta transición, especifica objetos EnterTransition
y ExitTransition
.
var visible by remember { mutableStateOf(true) } val density = LocalDensity.current AnimatedVisibility( visible = visible, enter = slideInVertically { // Slide in from 40 dp from the top. with(density) { -40.dp.roundToPx() } } + expandVertically( // Expand from the top. expandFrom = Alignment.Top ) + fadeIn( // Fade in with the initial alpha of 0.3f. initialAlpha = 0.3f ), exit = slideOutVertically() + shrinkVertically() + fadeOut() ) { Text( "Hello", Modifier .fillMaxWidth() .height(200.dp) ) }
Como se muestra en el ejemplo anterior, puedes combinar varios objetos EnterTransition
o ExitTransition
con un operador +
, y cada uno acepta parámetros opcionales para personalizar su comportamiento. Consulta las páginas de referencia para obtener más información.
Ejemplos de transiciones de entrada y salida
AnimatedVisibility
también ofrece una variante que toma un argumento de MutableTransitionState
. De esta manera, puedes activar una animación en cuanto se agrega el elemento AnimatedVisibility
componible al árbol de composición. También es útil para observar el estado de la animación.
// Create a MutableTransitionState<Boolean> for the AnimatedVisibility. val state = remember { MutableTransitionState(false).apply { // Start the animation immediately. targetState = true } } Column { AnimatedVisibility(visibleState = state) { Text(text = "Hello, world!") } // Use the MutableTransitionState to know the current animation state // of the AnimatedVisibility. Text( text = when { state.isIdle && state.currentState -> "Visible" !state.isIdle && state.currentState -> "Disappearing" state.isIdle && !state.currentState -> "Invisible" else -> "Appearing" } ) }
Cómo animar la entrada y la salida de elementos secundarios
El contenido dentro de AnimatedVisibility
(elementos secundarios directos o indirectos) puede usar el modificador animateEnterExit
a fin de especificar un comportamiento de animación diferente para cada uno de ellos. El efecto visual para cada uno de estos elementos secundarios es una combinación de las animaciones que se especifican en el elemento AnimatedVisibility
que admite composición y las animaciones de entrada y salida propias del elemento secundario.
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut() ) { // Fade in/out the background and the foreground. Box( Modifier .fillMaxSize() .background(Color.DarkGray) ) { Box( Modifier .align(Alignment.Center) .animateEnterExit( // Slide in/out the inner box. enter = slideInVertically(), exit = slideOutVertically() ) .sizeIn(minWidth = 256.dp, minHeight = 64.dp) .background(Color.Red) ) { // Content of the notification… } } }
En algunos casos, es posible que quieras que AnimatedVisibility
no aplique ninguna animación de modo que cada elemento secundario pueda tener sus propias animaciones distintas mediante animateEnterExit
. Para lograrlo, especifica EnterTransition.None
y ExitTransition.None
en el elemento AnimatedVisibility
que admite composición.
Cómo agregar animación personalizada
Si quieres agregar efectos de animación personalizada además de las animaciones de entrada y salida integradas, accede a la instancia Transition
subyacente a través de la propiedad transition
dentro de la lambda de contenido para AnimatedVisibility
. Todos los estados de animación que se agreguen a la instancia Transition se ejecutarán de manera simultánea con las animaciones de entrada y salida de AnimatedVisibility
. AnimatedVisibility
espera hasta que terminen todas las animaciones en Transition
antes de quitar su contenido.
En el caso de las animaciones de salida que se crean independientemente de Transition
(por ejemplo, mediante animate*AsState
), AnimatedVisibility
no podría responder por ellas y, por lo tanto, es posible que quite el contenido que admite composición antes de finalizar.
var visible by remember { mutableStateOf(true) } AnimatedVisibility( visible = visible, enter = fadeIn(), exit = fadeOut() ) { // this: AnimatedVisibilityScope // Use AnimatedVisibilityScope#transition to add a custom animation // to the AnimatedVisibility. val background by transition.animateColor(label = "color") { state -> if (state == EnterExitState.Visible) Color.Blue else Color.Gray } Box( modifier = Modifier .size(128.dp) .background(background) ) }
Para obtener más información sobre el uso de Transition
para administrar animaciones, consulta Cómo animar varias propiedades de forma simultánea con una transición.
Animar en función del estado objetivo
El elemento AnimatedContent
que admite composición anima su contenido a medida que cambia en función de un estado objetivo.
Row { var count by remember { mutableIntStateOf(0) } Button(onClick = { count++ }) { Text("Add") } AnimatedContent( targetState = count, label = "animated content" ) { targetCount -> // Make sure to use `targetCount`, not `count`. Text(text = "Count: $targetCount") } }
De forma predeterminada, el contenido inicial aplica un fundido de salida y, luego, el contenido objetivo aplica un fundido de entrada (este comportamiento se denomina atenuación). Puedes personalizar este comportamiento de animación si especificas un objeto ContentTransform
en el parámetro transitionSpec
. Puedes crear una instancia de ContentTransform
combinando un objeto EnterTransition
con un objeto ExitTransition
a través de la función infija with
. Puedes aplicar SizeTransform
al objeto ContentTransform
si lo adjuntas con la función infija using
.
AnimatedContent( targetState = count, transitionSpec = { // Compare the incoming number with the previous number. if (targetState > initialState) { // If the target number is larger, it slides up and fades in // while the initial (smaller) number slides up and fades out. slideInVertically { height -> height } + fadeIn() togetherWith slideOutVertically { height -> -height } + fadeOut() } else { // If the target number is smaller, it slides down and fades in // while the initial number slides down and fades out. slideInVertically { height -> -height } + fadeIn() togetherWith slideOutVertically { height -> height } + fadeOut() }.using( // Disable clipping since the faded slide-in/out should // be displayed out of bounds. SizeTransform(clip = false) ) }, label = "animated content" ) { targetCount -> Text(text = "$targetCount") }
EnterTransition
define cómo debe aparecer el contenido objetivo, y ExitTransition
define cómo debe desaparecer el contenido inicial. Además de todas las funciones EnterTransition
y ExitTransition
disponibles para AnimatedVisibility
, AnimatedContent
ofrece slideIntoContainer
y slideOutOfContainer
.
Estas son alternativas convenientes para slideInHorizontally/Vertically
y slideOutHorizontally/Vertically
que calculan la distancia de la diapositiva según los tamaños del contenido inicial y el contenido objetivo del contenido AnimatedContent
.
SizeTransform
define cómo se debe animar el tamaño entre el contenido inicial y el objetivo. Cuando creas la animación, tienes acceso al tamaño inicial y al tamaño objetivo. SizeTransform
también controla si el contenido debe recortarse según el tamaño del componente durante las animaciones.
var expanded by remember { mutableStateOf(false) } Surface( color = MaterialTheme.colorScheme.primary, onClick = { expanded = !expanded } ) { AnimatedContent( targetState = expanded, transitionSpec = { fadeIn(animationSpec = tween(150, 150)) togetherWith fadeOut(animationSpec = tween(150)) using SizeTransform { initialSize, targetSize -> if (targetState) { keyframes { // Expand horizontally first. IntSize(targetSize.width, initialSize.height) at 150 durationMillis = 300 } } else { keyframes { // Shrink vertically first. IntSize(initialSize.width, targetSize.height) at 150 durationMillis = 300 } } } }, label = "size transform" ) { targetExpanded -> if (targetExpanded) { Expanded() } else { ContentIcon() } } }
Cómo animar las transiciones de entrada y salida de elementos secundarios
Al igual que AnimatedVisibility
, el modificador animateEnterExit
está disponible dentro de la lambda de contenido de AnimatedContent
. Úsalo para aplicar EnterAnimation
y ExitAnimation
a cada uno de los elementos secundarios directos o indirectos por separado.
Cómo agregar animación personalizada
Al igual que AnimatedVisibility
, el campo transition
está disponible dentro de la lambda de contenido de AnimatedContent
. Úsalo para crear un efecto de animación personalizado que se ejecuta de manera simultánea con la transición AnimatedContent
. Consulta updateTransition para obtener más detalles.
Cómo animar entre dos diseños
Crossfade
anima entre dos diseños con una animación de encadenado. Si alternas el valor que se pasa al parámetro current
, el contenido se cambia con una animación de encadenado.
var currentPage by remember { mutableStateOf("A") } Crossfade(targetState = currentPage, label = "cross fade") { screen -> when (screen) { "A" -> Text("Page A") "B" -> Text("Page B") } }
Modificadores de animación integrados
Compose proporciona modificadores para animar cambios específicos directamente en elementos componibles.
Cómo animar los cambios de tamaño de los elementos componibles

El modificador animateContentSize
anima un cambio de tamaño.
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
Animaciones de elementos de lista
Si deseas animar los reordenamientos de elementos dentro de una lista o cuadrícula diferida, consulta la documentación de animación de elementos de diseño diferido.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Animaciones basadas en el valor
- Animaciones en Compose
- Compatibilidad con herramientas de animación {:#tooling}