compose-motion
Jetpack Compose animation foundations - animate*AsState, AnimatedVisibility, Crossfade, updateTransition, SharedTransitionLayout, gestures.
By athevon · 833 installs
npx skills add athevon/genjutsu --skill compose-motion
Source repository · Upstream listing
Version sensitive. Every API name, SDK gate and browser support claim below was
verified on 2026 09 08 against primary sources. What against, and when, is in
jutsu/VERSIONS.md . If that date is old, re verify before acting on a version number.
Compose Motion Sub skill
Jetpack Compose animation core. Loaded for Android Compose and Compose Multiplatform projects.
Concise rules here. Deep dive in references/ .
Baseline: Jetpack Compose 1.12.0 (current stable, Aug 2026) i.e. Compose BOM 2026.08.x. Modern stable APIs only no swipeable , no animateContentSize hacks where AnimatedContent is correct. Anything below flagged 'Compose 1.x+' is a since marker, not the target.
API Decision Tree
Need API
Single value over time animateFloatAsState , animateDpAsState , animateColorAsState , etc.
Visibility / mount unmount AnimatedVisibility(visible) { ... }
Crossfade between states Crossfade(target) { state ... }
Multi state coordinated updateTransition(target).animateFloat { ... }
Manual control / interruption Animatable(initialValue) + animateTo(...)
Looping / infinite rememberInfiniteTransition().animateFloat(...)
Shared elements SharedTransitionLayout + Modifier.sharedElement(...) (Compose 1.7+)
Layout swap with anim AnimatedContent(target) { ... }
Drag / swipe Modifier.draggable + Animatable.snapTo / animateTo , or Modifier.anchoredDraggable for snap points
Rule: climb the ladder only when needed. animate AsState covers 70% of cases. Reach for Animatable only when you need to interrupt, chain, or read velocity.
Spring API (opinionated defaults)
Use Spec
UI snap (modal, drawer, tab) spring(stiffness = Spring.StiffnessMediumLow, dampingRatio = Spring.DampingRatioNoBouncy)
Tactile (button press release, toggle) spring(stiffness = Spring.StiffnessMedium, dampingRatio = 0.85f)
Bouncy reveal (toast, FAB, success) spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy)
Drag follow (1:1 finger tracking) spring(stiffness = Spring.StiffnessHigh, dampingRatio = 1f)
Stiffness constants ( androidx.compose.animation.core.Spring ): StiffnessVeryLow 50f, StiffnessLow 200f, StiffnessMediumLow 400f, StiffnessMedium 1500f (the spring() default), StiffnessHigh 10000f. Higher = faster settle. Damping constants: DampingRatioHighBouncy 0.2f, DampingRatioMediumBouncy 0.5f, DampingRatioLowBouncy 0.75f, DampingRatioNoBouncy 1.0f (the default). Below 1.0 overshoots. Springs ignore durationMillis ; if you need a deterministic duration, use tween(...) instead.
animate AsState The Bread and Butter
The label shows up in Layout Inspector / Animation Preview always set it, future you will thank present you. Variants ship for Dp , Color , Offset , IntOffset , Size , Rect , Float , Int , and a generic animateValueAsState for custom types via TwoWayConverter .
AnimatedVisibility Mount / Unmount with Anim
Combine multiple enter/exit transitions with + . Respect motion principles: exit shorter and simpler than enter (here 150ms tween fade vs full slide+fade enter). The content composable only runs while visible OR animating safe to mount expensive children inside.
AnimatedContent State Driven Layout Swap
togetherWith runs enter and exit in parallel; using SizeTransform(clip = false) controls how the container resizes between contents. Keys matter: if targetState doesn't change identity, no transition fires.
Crossfade Simple Fade Between States
Use when you only need a fade. For anything richer (slide, scale, layout aware), reach for AnimatedContent . Crossfade does NOT animate size the container takes the size of the new content immediately.
updateTransition Multi Property Coordinated
Use when several properties animate together based on the same state. All children share the same transition timeline, so they finish in sync. Each animate call accepts its own transitionSpec lambda for per property tuning.
Animatable Manual Control
Reach for Animatable when you need to interrupt ( stop() ), chain ( animateTo returns when finished), read live velocity, or kick off decay ( animateDecay ). It is the imperative escape hatch for drag then fling, snap back, and any flow animate AsState cannot express.
SharedTransitionLayout (Compose 1.7+) Hero Animations
Two scopes plumbed down: SharedTransitionScope (where Modifier.sharedElement extension lives) and AnimatedVisibilityScope (the visibility context that drives the transition). rememberSharedContentState(key) keys MUST match across screens or no animation fires. Deep dive in references/shared transitions.md .
InfiniteTransition Looping
RepeatMode.Restart jumps back to start each cycle; RepeatMode.Reverse ping pongs. Always provide a pause / off path for accessibility (see Reduced Motion section below). For one shot decay (fling), use Animatable.animateDecay instead.
Gestures (quick map)
Need Modifier
Tap Modifier.clickable(onClick = ...)
Tap + long press Modifier.combinedClickable(onClick = ..., onLongClick = ...)
Drag (single axis) Modifier.draggable(state, orientation)
Multi touch / custom Modifier.pointerInput { detectDragGestures { ... } }
Scroll offset reading LazyListState or ScrollState.value
Swipe to dismiss / snap points Modifier.anchoredDraggable(state) (Compose 1.6+, replaces swipeable )
Pinch + pan + rotate Modifier.transformable(state)
Deep dive in references/gestures compose.md (NestedScrollConnection, conflict resolution, fling decay).
Anti Patterns (BAD / GOOD)
1. Animating layout size instead of transform
If the actual layout size MUST change (parent depends on it), use Modifier.animateContentSize() so neighbors animate too, or wrap the swap in AnimatedContent .
2. LaunchedEffect(true) / LaunchedEffect(Unit) with hidden inputs
If you genuinely want "run once", use LaunchedEffect(Unit) on purpose AND ensure you don't capture varying state or hoist the captured values out. When in doubt, key on the values you read.
3. Forgetting key in a LazyColumn with item animations
Modifier.animateItem() (Compose 1.7+, replaces animateItemPlacement ) handles insert/remove/move automatically when key is stable.
4. Nesting AnimatedContent inside scrolling list items
Rule: heavy animation containers ( AnimatedContent , SharedTransitionLayout ) belong at screen scope, not per row.
Reduced Motion Respect It
See ../motion principles/SKILL.md for the cross platform doctrine.
ANIMATOR DURATION SCALE reads the animator duration scale directly. The cleaner call is ValueAnimator.areAnimatorsEnabled() (API 26+), which returns false when that scale is 0 set by the developer options "Animation off" toggle, Battery Saver, and the user facing "Remove animations" (Settings Accessibility) toggle alike. Deep dive in ../mobile principles/references/accessibility mobile.md .
Quick Reference
Need Load
Shared transitions deep dive references/shared transitions.md
Gestures + nestedScroll patterns references/gestures compose.md
Recomposition / jank / Layout Inspector references/recomposition and anim.md
Advanced (M3 Expressive, AGSL, Canvas) ../compose graphics/SKILL.md
CMP / KMP patterns ../compose multiplatform/SKILL.md
Mobile UX context ../mobile principles/SKILL.md
Foundation (timing, easing, a11y) ../motion principles/SKILL.md
Sources
[aldefy/compose skill](https://github.com/aldefy/compose skill) animation reference
[skydoves/Orbital](https://github.com/skydoves/Orbital) shared element transitions
[mutualmobile/compose animation examples](https://github.com/mutualmobile/compose animation examples)
[fornewid/material motion compose](https://github.com/fornewid/material motion compose)
[Android Compose animations docs](https://developer.android.com/develop/ui/compose/animation/introduction)
[Android SharedTransitionLayout](https://developer.android.com/develop/ui/compose/animation/shared elements)