O Compose tem muitos mecanismos de animação integrados, e pode ser difícil saber qual escolher. Confira abaixo uma lista de casos de uso comuns de animação. Para informações mais detalhadas sobre o conjunto completo de diferentes opções de API disponíveis para você, leia a documentação completa do Compose Animation.
Animar propriedades combináveis comuns
O Compose oferece APIs convenientes que permitem resolver muitos casos de uso de animação comuns. Esta seção demonstra como animar propriedades comuns de um elemento combinável.
Animar aparecimento / desaparecimento

Use AnimatedVisibility
para ocultar ou mostrar um elemento combinável. Os filhos dentro de
AnimatedVisibility
podem usar Modifier.animateEnterExit()
para a própria transição de entrada
ou saída.
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 // ... }
Os parâmetros de entrada e saída de AnimatedVisibility
permitem configurar o comportamento
de um elemento combinável quando ele aparece e desaparece. Leia a documentação completa para mais informações.
Outra opção para animar a visibilidade de um elemento combinável é animar o
alfa ao longo do tempo usando animateFloatAsState
:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
No entanto, mudar o alfa tem a ressalva de que o elemento combinável permanece
na composição e continua ocupando o espaço em que está disposto. Isso
pode fazer com que leitores de tela e outros mecanismos de acessibilidade ainda considerem
o item na tela. Por outro lado, AnimatedVisibility
acaba removendo o item da composição.

Animar a cor do plano de fundo

val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Essa opção tem um desempenho melhor do que usar Modifier.background()
.
Modifier.background()
é aceitável para uma configuração de cor única, mas ao
animar uma cor ao longo do tempo, isso pode causar mais recomposições do que
o necessário.
Para animar infinitamente a cor de fundo, consulte a seção repetir uma animação.
Animar o tamanho de um elemento combinável

O Compose permite animar o tamanho de elementos combináveis de algumas maneiras diferentes. Use
animateContentSize()
para animações entre mudanças de tamanho combináveis.
Por exemplo, se você tiver uma caixa com texto que pode se expandir de uma para
várias linhas, use Modifier.animateContentSize()
para conseguir uma transição
mais suave:
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 } ) { }
Você também pode usar AnimatedContent
com um SizeTransform
para descrever
como as mudanças de tamanho devem ocorrer.
Animar a posição do elemento combinável

Para animar a posição de um elemento combinável, use Modifier.offset{ }
combinado com
animateIntOffsetAsState()
.
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
Se você quiser garantir que os elementos combináveis não sejam desenhados sobre ou sob outros
elementos combináveis ao animar posição ou tamanho, use Modifier.layout{ }
. Esse modificador propaga mudanças de tamanho e posição para o elemento pai, o que afeta outros elementos filhos.
Por exemplo, se você estiver movendo um Box
dentro de um Column
e os outros filhos
precisarem se mover quando o Box
se mover, inclua as informações de deslocamento com
Modifier.layout{ }
da seguinte maneira:
var toggled by remember { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } Column( modifier = Modifier .padding(16.dp) .fillMaxSize() .clickable(indication = null, interactionSource = interactionSource) { toggled = !toggled } ) { val offsetTarget = if (toggled) { IntOffset(150, 150) } else { IntOffset.Zero } val offset = animateIntOffsetAsState( targetValue = offsetTarget, label = "offset" ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) Box( modifier = Modifier .layout { measurable, constraints -> val offsetValue = if (isLookingAhead) offsetTarget else offset.value val placeable = measurable.measure(constraints) layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) { placeable.placeRelative(offsetValue) } } .size(100.dp) .background(colorGreen) ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) }

Modifier.layout{ }
Animar o padding de um elemento combinável

Para animar o padding de um elemento combinável, use animateDpAsState
combinado com
Modifier.padding()
:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
Animar a elevação de um elemento combinável
Para animar a elevação de um elemento combinável, use animateDpAsState
combinado com
Modifier.graphicsLayer{ }
. Para mudanças de elevação únicas, use
Modifier.shadow()
. Se você estiver animando a sombra, use o modificador
Modifier.graphicsLayer{ }
, que é a opção mais eficiente.
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
Outra opção é usar o elemento combinável Card
e definir a propriedade de elevação para
valores diferentes por estado.
Animar escala, translação ou rotação de texto

Ao animar a escala, a translação ou a rotação do texto, defina o parâmetro textMotion
em TextStyle
como TextMotion.Animated
. Isso garante transições mais suaves entre animações de texto. Use Modifier.graphicsLayer{ }
para
traduzir, girar ou dimensionar o texto.
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val scale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 8f, animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "scale" ) Box(modifier = Modifier.fillMaxSize()) { Text( text = "Hello", modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale transformOrigin = TransformOrigin.Center } .align(Alignment.Center), // Text composable does not take TextMotion as a parameter. // Provide it via style argument but make sure that we are copying from current theme style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated) ) }
Animar a cor do texto

Para animar a cor do texto, use a lambda color
no elemento combinável BasicText
:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
Alternar entre diferentes tipos de conteúdo

Use AnimatedContent
para animar entre diferentes elementos combináveis. Se você
quiser apenas um efeito de transição padrão entre elementos combináveis, use Crossfade
.
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
O AnimatedContent
pode ser personalizado para mostrar muitos tipos diferentes de transições de entrada e
saída. Para mais informações, leia a documentação sobre
AnimatedContent
ou esta postagem do blog sobre
AnimatedContent
.
Animar durante a navegação para diferentes destinos

Para animar transições entre elementos combináveis ao usar o
artefato navigation-compose, especifique enterTransition
e
exitTransition
em um elemento combinável. Você também pode definir a animação padrão a ser
usada para todos os destinos no nível superior NavHost
:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
Há muitos tipos diferentes de transições de entrada e saída que aplicam efeitos diferentes ao conteúdo de entrada e saída. Consulte a documentação para mais informações.
Repetir uma animação

Use rememberInfiniteTransition
com um infiniteRepeatable
animationSpec
para repetir sua animação continuamente. Mude RepeatModes
para
especificar como ele deve ir e voltar.
Use finiteRepeatable
para repetir um número definido de vezes.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
Iniciar uma animação na inicialização de um elemento combinável
O LaunchedEffect
é executado quando um elemento combinável entra na composição. Ele inicia
uma animação ao iniciar um elemento combinável. Você pode usar isso para acionar a mudança de estado
da animação. Usando Animatable
com o método animateTo
para iniciar a
animação na inicialização:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Criar animações sequenciais

Use as APIs de corrotina Animatable
para realizar animações
sequenciais ou simultâneas. Chamar animateTo
no Animatable
um após o outro faz com que
cada animação aguarde a conclusão das animações anteriores antes de prosseguir .
Isso ocorre porque ela é uma função de suspensão.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Criar animações simultâneas

Use as APIs de corrotina (Animatable#animateTo()
ou animate
) ou
a API Transition
para conseguir animações simultâneas. Se você usar várias
funções de inicialização em um contexto de corrotina, as animações serão iniciadas ao mesmo
tempo:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Você pode usar a API updateTransition
para usar o mesmo estado e acionar várias animações de propriedades diferentes ao mesmo tempo. O exemplo abaixo anima
duas propriedades controladas por uma mudança de estado, rect
e borderWidth
:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "rect") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "borderWidth") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
Otimizar o desempenho da animação
Animações no Compose podem causar problemas de desempenho. Isso acontece devido à natureza de uma animação: mover ou mudar pixels na tela rapidamente, frame a frame, para criar a ilusão de movimento.
Considere as diferentes fases do Compose: composição, layout e desenho. Se a animação mudar a fase de layout, todos os elementos combináveis afetados precisarão ser refeitos e redesenhados. Se a animação ocorrer na fase de desenho, ela será mais eficiente por padrão do que se você a executasse na fase de layout, já que teria menos trabalho a fazer no geral.
Para garantir que seu app faça o mínimo possível durante a animação, escolha a versão lambda de um Modifier
sempre que possível. Isso ignora a recomposição e realiza
a animação fora da fase de composição. Caso contrário, use
Modifier.graphicsLayer{ }
, já que esse modificador sempre é executado na fase
de desenho. Para mais informações, consulte a seção adiamento de leituras na documentação de desempenho.
Alterar o tempo da animação
Por padrão, o Compose usa animações de mola para a maioria das animações. As molas ou animações baseadas na física parecem mais naturais. Elas também podem ser interrompidas, já que consideram a velocidade atual do objeto em vez de um tempo fixo.
Se quiser substituir o padrão, todas as APIs de animação demonstradas acima
podem definir um animationSpec
para personalizar a execução de uma animação,
seja por uma determinada duração ou com mais elasticidade.
Confira um resumo das diferentes opções de animationSpec
:
spring
: animação baseada em física, o padrão para todas as animações. Você pode mudar a rigidez ou a dampingRatio para conseguir uma aparência diferente da animação.tween
(abreviação de entre): animação baseada em duração, anima entre dois valores com uma funçãoEasing
.keyframes
: especificação para determinar valores em determinados pontos principais de uma animação.repeatable
: especificação baseada em duração que é executada um determinado número de vezes, especificado porRepeatMode
.infiniteRepeatable
: especificação baseada em duração que é executada para sempre.snap
: passa instantaneamente para o valor final sem animação.

Leia a documentação completa para mais informações sobre animationSpecs.
Outros recursos
Para mais exemplos de animações divertidas no Compose, confira o seguinte:
- 5 animações rápidas no Compose
- Como fazer Jellyfish se mover no Compose (link em inglês)
- Personalizar
AnimatedContent
no Compose - Funções de easing no Compose