On this page
- Which API should you use?
- 1. animate*AsState: animate a single value
- 2. AnimatedVisibility: enter and exit
- 3. AnimatedContent: swap content
- 4. animateContentSize: smooth expand and collapse
- 5. updateTransition: coordinate several values
- 6. Infinite animations: use sparingly
- Animation specs in one minute
- Motion and accessibility
- Where animation helps users
- Common mistakes
- Frequently asked questions
- Conclusion
In the View system, animating meant ObjectAnimators, listeners and careful cleanup. In Compose, animation follows the same idea as everything else: you change state, and the animation APIs smoothly move the UI from the old value to the new one.
Which API should you use?
Animatable only for gestures or custom choreography.1. animate*AsState: animate a single value
@Composable
fun FavoriteButton(isFavorite: Boolean, onToggle: () -> Unit) {
val scale by animateFloatAsState(
targetValue = if (isFavorite) 1.2f else 1f,
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
label = "favoriteScale",
)
val tint by animateColorAsState(
targetValue = if (isFavorite) Color(0xFFE11D48) else MaterialTheme.colorScheme.onSurfaceVariant,
label = "favoriteTint",
)
IconButton(onClick = onToggle) {
Icon(
imageVector = if (isFavorite) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder,
contentDescription = if (isFavorite) "Remove from favourites" else "Add to favourites",
tint = tint,
modifier = Modifier.graphicsLayer { scaleX = scale; scaleY = scale },
)
}
}Change the target and Compose animates to it, even if it changes again mid-animation. Using graphicsLayer applies the scale in the draw phase, which is cheaper than changing layout size.
2. AnimatedVisibility: enter and exit
AnimatedVisibility(
visible = state.error != null,
enter = slideInVertically { -it } + fadeIn(),
exit = slideOutVertically { -it } + fadeOut(),
) {
ErrorBanner(message = state.error.orEmpty(), onRetry = onRetry)
}3. AnimatedContent: swap content
AnimatedContent(
targetState = cartCount,
transitionSpec = {
if (targetState > initialState) slideInVertically { it } + fadeIn() togetherWith slideOutVertically { -it } + fadeOut()
else slideInVertically { -it } + fadeIn() togetherWith slideOutVertically { it } + fadeOut()
},
label = "cartCount",
) { count -> Text("$count", style = MaterialTheme.typography.titleLarge) }4. animateContentSize: smooth expand and collapse
var expanded by remember { mutableStateOf(false) }
Text(
text = longDescription,
maxLines = if (expanded) Int.MAX_VALUE else 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.animateContentSize().clickable { expanded = !expanded },
)5. updateTransition: coordinate several values
val transition = updateTransition(targetState = selected, label = "cardSelection")
val elevation by transition.animateDp(label = "elevation") { if (it) 8.dp else 1.dp }
val borderWidth by transition.animateDp(label = "border") { if (it) 2.dp else 0.dp }
val bg by transition.animateColor(label = "bg") {
if (it) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surface
}6. Infinite animations: use sparingly
val infinite = rememberInfiniteTransition(label = "skeleton")
val alpha by infinite.animateFloat(
initialValue = 0.4f, targetValue = 0.9f,
animationSpec = infiniteRepeatable(tween(800), RepeatMode.Reverse),
label = "skeletonAlpha",
)
Box(Modifier.fillMaxWidth().height(16.dp).graphicsLayer { this.alpha = alpha }.background(Color.LightGray))Animation specs in one minute
spring(): physics-based and the default for most APIs. It handles interruptions naturally.tween(durationMillis, easing): a fixed duration with an easing curve. Good for fades.keyframes { }: specific values at specific times.
Motion and accessibility
Some users enable Remove animations in system settings. Compose's animation system respects the device's animator duration scale. Keep motion purposeful: short UI transitions (roughly 150โ300 ms) feel responsive. Never hide essential information inside an animation, and avoid large, continuous movement.
Where animation helps users
Feedback
A heart scaling on tap confirms the action happened.
Continuity
Expanding cards and list item animations show where content went.
Status
Skeleton placeholders and progress indicators set expectations while loading.
Common mistakes
- Animating layout-affecting values (padding, size) every frame when a
graphicsLayertransform would be cheaper. - Infinite animations left running off-screen or behind dialogs.
- Omitting
label, which makes Animation Preview and the inspector harder to use. - Over-animating: everything bouncing distracts and slows users down.
Frequently asked questions
Can I preview animations?
Yes. Android Studio's Animation Preview lets you inspect and scrub transitions, which is where labels help.
How do I animate between screens?
Navigation libraries provide enter and exit transitions, and shared element transitions are available in Compose for matching elements across screens.
Are Compose animations performant?
Yes, when they animate draw-phase properties (alpha, translation, scale via graphicsLayer) and avoid recomposing large trees every frame.
Conclusion
Compose animation is state-driven: change the state and let animate*AsState, AnimatedVisibility, AnimatedContent, animateContentSize, updateTransition or an infinite transition interpolate. Prefer high-level APIs, draw-phase transforms and purposeful, short motion.
Next step: make your app look polished and personal with Material 3 Theming and Dynamic Colors.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Jetpack Compose clicks once you build real screens with real state. Practise modern Android UI development on projects that go beyond toy examples.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.