Understanding Property Animations in Jetpack Compose: animateColorAsState and Related APIs
This article explains how Jetpack Compose implements property animations by detailing the animateColorAsState function, its parameters, related animation helpers such as animateDpAsState and animateValueAsState, and the underlying Animatable, TargetBasedAnimation, and runAnimation mechanisms, complete with Kotlin code examples.
Jetpack Compose provides a comprehensive set of APIs for property animations, allowing developers to animate values by smoothly transitioning between a start and an end state. This article focuses on the animateColorAsState function and explores the related animation helpers offered by Compose.
animateColorAsState API
The function signature is:
@Composable
fun animateColorAsState(
targetValue: Color,
animationSpec: AnimationSpec<Color> = colorDefaultSpring,
label: String = "ColorAnimation",
finishedListener: ((Color) -> Unit)? = null
): State<Color> {
val converter = remember(targetValue.colorSpace) {
(Color.VectorConverter)(targetValue.colorSpace)
}
return animateValueAsState(
targetValue, converter, animationSpec, label = label, finishedListener = finishedListener
)
}It accepts four parameters: targetValue (required), animationSpec , label , and finishedListener . The function delegates the work to animateValueAsState after converting the color to an AnimationVector using a TwoWayConverter .
Other animation helpers
Compose also provides similar functions for other types, such as:
@Composable
fun animateDpAsState(
targetValue: Dp,
animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
label: String = "DpAnimation",
finishedListener: ((Dp) -> Unit)? = null
): State<Dp> {
return animateValueAsState(
targetValue,
Dp.VectorConverter,
animationSpec,
label = label,
finishedListener = finishedListener
)
}
@Composable
fun animateIntAsState(
targetValue: Int,
animationSpec: AnimationSpec<Int> = intDefaultSpring,
label: String = "IntAnimation",
finishedListener: ((Int) -> Unit)? = null
) { /* ... */ }
@Composable
fun animateSizeAsState(
targetValue: Size,
animationSpec: AnimationSpec<Size> = sizeDefaultSpring,
label: String = "SizeAnimation",
finishedListener: ((Size) -> Unit)? = null
) { /* ... */ }
@Composable
fun animateRectAsState(
targetValue: Rect,
animationSpec: AnimationSpec<Rect> = rectDefaultSpring,
label: String = "RectAnimation",
finishedListener: ((Rect) -> Unit)? = null
) { /* ... */ }All these helpers ultimately call animateValueAsState , which is the core implementation handling generic type animation.
Core implementation: animateValueAsState
@Composable
fun
animateValueAsState(
targetValue: T,
typeConverter: TwoWayConverter
,
animationSpec: AnimationSpec
= remember { spring() },
visibilityThreshold: T? = null,
label: String = "ValueAnimation",
finishedListener: ((T) -> Unit)? = null
): State
{
// implementation details
}The function creates an Animatable instance, remembers the listener, and sets up a Channel to receive target values. It then launches a coroutine that animates to new targets using animatable.animateTo and invokes the listener upon completion.
Animatable class
class Animatable
(
initialValue: T,
val typeConverter: TwoWayConverter
,
private val visibilityThreshold: T? = null,
val label: String = "Animatable"
)The animateTo method builds a TargetBasedAnimation with the provided spec and runs it via runAnimation , returning an AnimationResult that contains the final state and the reason the animation ended.
runAnimation and animation loop
private suspend fun runAnimation(
animation: Animation
,
initialVelocity: T,
block: (Animatable
.() -> Unit)?
): AnimationResult
{
val startTime = internalState.lastFrameTimeNanos
return mutatorMutex.mutate {
try {
// animation loop updating internalState
endState.animate(animation, startTime) {
updateState(internalState)
}
val endReason = if (clampingNeeded) BoundReached else Finished
endAnimation()
AnimationResult(endState, endReason)
} catch (e: CancellationException) {
endAnimation()
throw e
}
}
}All type‑specific animation paths converge here, ensuring a consistent execution model across color, Dp, Int, Size, Rect, and custom types.
The article concludes that property animations in Compose share a common generic infrastructure; differences lie mainly in the generic type and specific converters used. Readers are encouraged to explore each helper function for deeper understanding.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
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.