Jetpack Compose Animation Tutorial
This Jetpack Compose animation tutorial demonstrates how to create state‑driven, visibility, size, cross‑fade, single‑value, and composite animations using declarative APIs such as remember, mutableStateOf, AnimatedVisibility, animateContentSize, animate*AsState, and updateTransition, showing concise Kotlin code that simplifies UI animation development.
Jetpack Compose is a new Android UI toolkit released by Google that simplifies and accelerates native UI development. It is a declarative UI framework that brings significant benefits such as more concise code, higher development efficiency, intuitive Kotlin APIs, and powerful preview tools.
Development Environment
The author uses Android Studio Canary to avoid extra configuration. The project is created with an Empty Compose Activity template. The build.gradle file must contain matching versions of com.android.tools.build:gradle and org.jetbrains.kotlin:kotlin-gradle-plugin :
dependencies {
classpath "com.android.tools.build:gradle:7.0.0-alpha15"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"
}Jetpack Compose Animations
Compose provides a rich set of animation APIs that can be used to create various effects. The tutorial covers several common animation techniques.
3.1 State‑driven Animation
Animations are driven by state changes. remember and mutableStateOf are used to observe state values. When the state changes, the UI recomposes and the animation runs automatically.
3.2 Visibility Animation – AnimatedVisibility
@ExperimentalAnimationApi
@Composable
fun AnimatedVisibility(
visible: Boolean,
modifier: Modifier = Modifier,
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = shrinkOut() + fadeOut(),
initiallyVisible: Boolean = visible,
content: @Composable () -> Unit
) {
AnimatedVisibilityImpl(visible, modifier, enter, exit, initiallyVisible, content)
}The default animation is fade‑in‑expand for entering and shrink‑out‑fade‑out for exiting. An example toggles an image’s visibility with a button:
@Composable
fun AnimationDemo() {
var visible by remember { mutableStateOf(true) }
Column(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { visible = !visible }) {
Text(text = if (visible) "Hide" else "Show")
}
Spacer(Modifier.height(16.dp))
AnimatedVisibility(
visible = visible,
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut()
) {
Image(painter = painterResource(id = R.drawable.pikaqiu), contentDescription = null, Modifier.fillMaxSize())
}
}
}3.3 Layout Size Animation – animateContentSize
fun Modifier.animateContentSize(
animationSpec: FiniteAnimationSpec
= spring(),
finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
)This extension on Modifier animates size changes. An example changes a Size variable with a button and applies animateContentSize() to a Box containing an image.
// Size animation example
var size by remember { mutableStateOf(Size(300F, 300F)) }
Column(Modifier.fillMaxSize(), Arrangement.Top, Alignment.CenterHorizontally) {
Button(onClick = {
size = if (size.height == 300F) Size(500F, 500F) else Size(300F, 300F)
}) { Text(if (size.height == 300F) "Shrink" else "Expand") }
Spacer(Modifier.height(16.dp))
Box(Modifier.animateContentSize()) {
Image(painter = painterResource(id = R.drawable.pikaqiu), contentDescription = null,
Modifier.animateContentSize().size(size.height.dp))
}
}3.4 Crossfade Layout Switching
// Crossfade animation example
var fadeStatus by remember { mutableStateOf(true) }
Column(Modifier.fillMaxSize(), Arrangement.Top, Alignment.CenterHorizontally) {
Button(onClick = { fadeStatus = !fadeStatus }) { Text(if (fadeStatus) "Fade In" else "Fade Out") }
Spacer(Modifier.height(16.dp))
Crossfade(targetState = fadeStatus, animationSpec = tween(3000)) { screen ->
when (screen) {
true -> Image(painter = painterResource(id = R.drawable.pikaqiu), contentDescription = null,
Modifier.animateContentSize().size(300.dp))
false -> Image(painter = painterResource(id = R.drawable.pikaqiu2), contentDescription = null,
Modifier.animateContentSize().size(300.dp))
}
}
}3.5 Single‑value Animations – animate*AsState
Functions such as animateColorAsState , animateDpAsState , and animateFloatAsState animate a single value from its current state to a target value.
// Example of animateFloatAsState
var transparent by remember { mutableStateOf(true) }
val alpha: Float by animateFloatAsState(if (transparent) 1f else 0.5f)
Column(Modifier.fillMaxSize(), Arrangement.Top, Alignment.CenterHorizontally) {
Button(onClick = { transparent = !transparent }) { Text(if (transparent) "Light" else "Dark") }
Spacer(Modifier.height(16.dp))
Box {
Image(painter = painterResource(id = R.drawable.pikaqiu), contentDescription = null,
Modifier.animateContentSize().graphicsLayer(alpha = alpha).size(300.dp))
}
}3.6 Composite Animation – updateTransition
var imagePosition by remember { mutableStateOf(ImagePosition.TopLeft) }
val transition = updateTransition(targetState = imagePosition, label = "")
val boxOffset by transition.animateOffset(label = "") { position ->
when (position) {
ImagePosition.TopLeft -> Offset(-60F, 0F)
ImagePosition.BottomRight -> Offset(60F, 120F)
ImagePosition.TopRight -> Offset(60F, 0F)
ImagePosition.BottomLeft -> Offset(-60F, 120F)
}
}
Button(onClick = { imagePosition = ChangePosition(imagePosition) }) { Text("Change position") }
Box {
Image(painter = painterResource(id = R.drawable.pikaqiu), contentDescription = null,
Modifier.offset(boxOffset.x.dp, boxOffset.y.dp).animateContentSize().size(300.dp))
}
enum class ImagePosition { TopRight, TopLeft, BottomRight, BottomLeft }
fun ChangePosition(position: ImagePosition) = when (position) {
ImagePosition.TopLeft -> ImagePosition.BottomRight
ImagePosition.BottomRight -> ImagePosition.TopRight
ImagePosition.TopRight -> ImagePosition.BottomLeft
ImagePosition.BottomLeft -> ImagePosition.TopLeft
}Conclusion
Jetpack Compose dramatically simplifies animation by allowing developers to declare UI changes in composable functions. The framework handles the underlying animation mechanics, leading to more readable code and faster development cycles.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.