Como adicionar animação a um app Android (Compose)
Por equipe Charmotion · Atualizado em

Para adicionar animação a um app Android, use primeiro as APIs nativas do Compose: animate*AsState para valores únicos, AnimatedVisibility para mostrar e ocultar e rememberInfiniteTransition para loops. Adicione lottie-compose para loops vetoriais criados por designers, app.rive:rive-android para personagens interativos e o Media3 ExoPlayer para clipes de vídeo.
Principais pontos
- A árvore de decisão do Google para o Compose associa cada caso a uma API, de
animate*AsStateaupdateTransition. - O MotionLayout é para interfaces baseadas em Views; a documentação dele indica a animação do Compose para projetos novos.
- O Google sugere um framework de terceiros, como o Lottie, para animações ilustradas complexas.
- A API do Rive para Compose está em beta; o
RiveAnimationViewlegado ainda oferece métodos simples de estado. - O Media3 tem um composable
Playerpara vídeo em loop.
Já tem um personagem ou logo? Veja seu personagem em telas reais de app: telas de splash, onboarding, carregamento e estado vazio. É grátis, roda no navegador e não envia nada.
Qual API de animação do Compose serve para o seu caso?
A página "Choose an animation API" do Google funciona como uma árvore de decisão. Em resumo:
| Caso | API |
|---|---|
| Um valor com destino conhecido (tamanho, cor, alfa) | animate*AsState, por exemplo animateFloatAsState |
| Mostrar ou ocultar conteúdo | AnimatedVisibility |
| Trocar um composable por outro | AnimatedContent ou Crossfade |
| Várias propriedades que mudam juntas | updateTransition |
| Um loop que roda sem parar | rememberInfiniteTransition |
| Movimento controlado por gestos | Animatable |
| Microanimação simples de ícone | AnimatedVectorDrawable |
| Arte ilustrada complexa | Um framework de terceiros, como o Lottie |
Acrescente uma linha que a tabela do Google não tem: um personagem que reage a eventos do app. Isso exige um runtime com estados, como o Rive, para que um único arquivo fique em idle, rode em loop durante o carregamento e reaja ao sucesso. Para um clipe fixo, use vídeo.
Se o seu app ainda usa Views, o MotionLayout continua disponível, mas a documentação dele agora começa com um aviso "Try the Compose way" que direciona para as APIs de animação do Compose.
Como funcionam o animate*AsState e o AnimatedVisibility?
Esses dois cobrem a maior parte do feedback de interface. O animateFloatAsState anima em direção ao destino que você passar, sempre que esse destino mudar:
@Composable
fun PressableCard(pressed: Boolean, showBanner: Boolean) {
val scale by animateFloatAsState(
targetValue = if (pressed) 0.95f else 1f,
label = "scale"
)
Column {
Card(Modifier.graphicsLayer { scaleX = scale; scaleY = scale }) { /* ... */ }
AnimatedVisibility(visible = showBanner) {
Text("Saved")
}
}
}
O AnimatedVisibility faz o conteúdo aparecer e desaparecer com fade e expansão por padrão, e aceita transições personalizadas de entrada e saída.
Quando vários valores precisam se mover juntos, como o tamanho, a cor e a elevação de um card ao ser selecionado, use updateTransition. Ele vincula todas as animações filhas a um único estado, então elas começam no mesmo instante e ficam sincronizadas.
Para loops, o rememberInfiniteTransition roda até o composable sair da tela:
val transition = rememberInfiniteTransition(label = "pulse")
val alpha by transition.animateFloat(
initialValue = 0.4f,
targetValue = 1f,
animationSpec = infiniteRepeatable(tween(800), RepeatMode.Reverse),
label = "alpha"
)
Box(Modifier.size(12.dp).alpha(alpha).background(Color.Gray, CircleShape))Como animar mudanças de conteúdo e ícones no Compose?
Quando um elemento da interface substitui outro, como um contador, um rótulo de status ou um estado vazio que vira uma lista, use AnimatedContent. Ele anima a saída do conteúdo antigo e a entrada do novo sempre que o estado de destino muda:
AnimatedContent(targetState = count, label = "count") { value ->
Text("$value items")
}
O Crossfade é uma alternativa mais simples: ele apenas faz um fade entre dois layouts, o que funciona bem para alternar entre abas ou entre os estados de carregamento e carregado. Para mudanças de tamanho dentro de um único composable, adicione Modifier.animateContentSize() e o contêiner cresce ou encolhe suavemente quando o conteúdo muda. Em listas, o Modifier.animateItem() anima os itens quando eles são inseridos, removidos ou reordenados.
Para ícones, a árvore de decisão do Google recomenda o AnimatedVectorDrawable para microanimações SVG simples, como um ícone de play que vira pause ou um check que se desenha sozinho. Ele é um recurso vector drawable com animators anexados, então é distribuído como XML e não precisa de nenhuma biblioteca extra. Use-o para movimentos de ícone pequenos e com uma única função, e deixe o Lottie para ilustrações completas.
As transições de navegação ficam a cargo da biblioteca de navegação: os destinos composable() do Compose Navigation aceitam enterTransition e exitTransition, então as trocas de tela são animadas sem código personalizado.
Como adicionar uma animação Lottie a uma tela Compose?
Adicione o artefato Compose do projeto lottie-android da Airbnb, usando a versão indicada no README:
implementation "com.airbnb.android:lottie-compose:$lottieVersion"
Coloque o .json exportado em res/raw/, carregue-o como uma composition e reproduza:
@Composable
fun LoadingAnimation() {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.loading)
)
LottieAnimation(
composition,
iterations = LottieConstants.IterateForever,
modifier = Modifier.size(160.dp)
)
}
A composition carrega de forma assíncrona, então composition é null nos primeiros frames; o LottieAnimation simplesmente não desenha nada até ela ficar pronta. Se você precisa controlar a reprodução, por exemplo para tocar uma vez quando uma tarefa terminar, use animateLottieCompositionAsState e passe o progresso dele para o LottieAnimation.
O Lottie funciona bem para arte linear: um spinner, um check de sucesso, uma ilustração de onboarding. Se um ícone só precisa de um morph simples, a árvore do Google indica o AnimatedVectorDrawable, que não precisa de biblioteca extra. Se a arte precisa mudar de comportamento conforme o estado do app, considere o Rive.

Como adicionar uma animação Rive e alternar estados no Android?
Adicione app.rive:rive-android (11.9.2 na documentação atual do Rive) junto com androidx.startup e coloque o .riv em res/raw/. Chame Rive.init(context) uma vez na inicialização.
A API do Rive para Compose está marcada como beta e é onde o Rive concentra os novos desenvolvimentos. Ela precisa de um worker, carrega o arquivo de forma assíncrona e renderiza assim que o carregamento é concluído:
setContent {
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.character),
riveWorker
)
when (riveFile) {
is Result.Loading -> CircularProgressIndicator()
is Result.Error -> Text("Animation unavailable")
is Result.Success -> Rive(riveFile.value)
}
}
No Compose, arquivos com view models são controlados pela API de Data Binding do Rive; siga a seção de Compose da documentação do Rive para Android para ver as chamadas atuais.
A View API legada, construída em torno de RiveAnimationView, continua com suporte e tem um método para cada tipo de input. Ela funciona em layouts XML ou dentro do AndroidView do Compose:
val mascot = findViewById<RiveAnimationView>(R.id.mascot)
mascot.setNumberState("CharacterStateMachine", "mood", 5f) // loading loop
mascot.fireState("CharacterStateMachine", "success") // one-shot
Os nomes diferenciam maiúsculas de minúsculas. O guia de Rive no Android, Flutter e React Native traz a configuração completa.
Como reproduzir um vídeo em loop com Media3?
Para um clipe pré-renderizado, use o Media3 ExoPlayer. A documentação atual usa androidx.media3:media3-exoplayer:1.11.1, e o media3-ui-compose-material3 oferece um composable Player:
import androidx.media3.common.Player as Media3Player
@Composable
fun LoopingClip(uri: String) {
val context = LocalContext.current
val exo = remember {
ExoPlayer.Builder(context).build().apply {
setMediaItem(MediaItem.fromUri(uri))
repeatMode = Media3Player.REPEAT_MODE_ONE
volume = 0f
prepare()
play()
}
}
DisposableEffect(Unit) { onDispose { exo.release() } }
Player(player = exo)
}
Definir volume como 0 deixa um loop decorativo em silêncio. Para clipes incluídos no app, use um MediaItem que aponte para o arquivo incluído em vez de uma URL remota, assim o clipe fica disponível offline e começa na hora.
Libere o player quando o composable sair da tela, senão ele continua decodificando em segundo plano. Vídeo é indicado para clipes com detalhes fotográficos ou 3D. Ele não reage a estados, então um personagem que precisa alternar entre idle e loading funciona melhor com Rive.
Como adicionar um mascote animado a um app Android?
Modele o mascote como uma função do estado da sua UI. A maioria dos apps já expõe um StateFlow a partir de um ViewModel; associe cada estado a um dos loops do mascote e dispare um trigger para eventos pontuais:
- Tela em repouso: mood 0. Requisição em andamento: mood 5 (loading).
- Requisição concluída: dispare
successouerrore depois volte para idle. - Entrada de voz: um loop listening; resposta tocando: um loop talking.
Mantenha esse mapeamento em um só lugar. Assim as telas apenas emitem estados, e a state machine do arquivo cuida das transições, então uma reação de sucesso nunca é cortada de repente.
O Charmotion gera esse tipo de arquivo a partir de um mascote ou logo enviado: um .riv com CharacterStateMachine (um input mood de 0 a 9, além de triggers como success, error, enter e exit) que funciona com as chamadas setNumberState e fireState acima, e um view model Character para Data Binding. Para planejar os estados, veja estados de animação de mascote.
Como manter as animações no Android fluidas e acessíveis?
Animações no Compose são leves quando alteram propriedades de desenho. Sempre que puder, anime alpha, escala e translação via graphicsLayer, e evite animar valores que obrigam um layout grande a ser medido de novo a cada frame.
- Pare loops infinitos e libere os players quando a tela não estiver visível.
- Use arquivos vetoriais pequenos para a arte da UI; deixe o vídeo para clipes que realmente precisam dele.
- Teste em um aparelho de entrada, não só em um emulador num notebook rápido.
Respeite as configurações de movimento do usuário. No Android 9 e posteriores, é possível ativar Configurações > Acessibilidade > Remover animações, o que define a escala de duração do animador do sistema como zero. Verifique Settings.Global.ANIMATOR_DURATION_SCALE e, quando for 0, mostre um frame estático do mascote em vez de um loop. Mantenha o significado, como "carregando" ou "salvo", disponível em texto.
O guia de movimento reduzido explica quais loops a WCAG espera que o usuário consiga pausar ou evitar.
Adicione animação a um app Jetpack Compose
- Escolha a API. Use a árvore de decisão do Google:
animate*AsState,AnimatedVisibility,updateTransitionourememberInfiniteTransition. - Adicione uma biblioteca para a arte. Adicione
com.airbnb.android:lottie-composepara Lottie ouapp.rive:rive-androidcomandroidx.startuppara Rive. - Inclua o arquivo no app. Coloque os arquivos
.jsone.rivemres/raw/com nomes em minúsculas e underscores. - Renderize. Use
LottieAnimation, o composableRive()do Rive depois querememberRiveFilefor concluído com sucesso, ou o composablePlayerdo Media3. - Controle os estados. Associe o estado do ViewModel a números de mood e triggers, por exemplo
setNumberStateefireStatena view legada. - Verifique as configurações de movimento. Mostre um frame estático quando a escala de duração do animador for 0.
Perguntas frequentes
O MotionLayout ainda é recomendado?
Só para telas baseadas em Views. O MotionLayout é construído sobre o ConstraintLayout e o sistema de Views, e a documentação dele agora começa com um aviso "Try the Compose way" que direciona projetos novos para as APIs de animação do Compose. Em um app Compose, use animate*AsState, AnimatedContent, updateTransition e APIs relacionadas.
AnimatedVectorDrawable ou Lottie?
Use AnimatedVectorDrawable para microanimações simples de ícones, como um ícone de play que se transforma em pause; não exige biblioteca extra. Use Lottie para animações ilustradas complexas feitas no After Effects. A árvore de decisão do Compose do Google traça a mesma linha entre ícones SVG simples e arte complexa.
Devo usar a API Compose do Rive ou o RiveAnimationView?
Use a API Compose em telas Compose novas, já que o Rive concentra o desenvolvimento nela, mas note que ela está marcada como beta. O RiveAnimationView continua com suporte e é a opção simples para layouts baseados em Views ou arquivos baseados em inputs controlados com setNumberState e fireState. O Compose pode hospedá-lo via AndroidView.
Como fazer uma animação no Compose rodar em loop para sempre?
Use rememberInfiniteTransition e anime um valor com infiniteRepeatable, por exemplo tween(800) com RepeatMode.Reverse para um efeito de pulsação. O loop roda enquanto o composable está na tela e para quando ele sai da composição. Para um arquivo Lottie, passe iterations = LottieConstants.IterateForever para LottieAnimation em vez de escrever seu próprio loop.
Respostas rápidas deste guia
Fontes
- Choose an animation API, Android Developers
- Manage motion and widget animation with MotionLayout, Android Developers
- lottie-android, Airbnb (GitHub)
- Android runtime, Rive
- Android runtime: Inputs (legacy), Rive
- Media3 ExoPlayer: Hello world, Android Developers