agentsclimarketplace

Compose animations

Skill almasumdev/awesome-kotlin-android-agent-skills/.github/skills/ui/compose-animations

Curated agent skills, conventions, and workflows for building Kotlin Android apps with AI coding agents.

Install
npx -y skills add almasumdev/awesome-kotlin-android-agent-skills --skill compose-animations

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Expert guidance on Compose animation APIs including animate*AsState, AnimatedVisibility, updateTransition, AnimatedContent, and shared element transitions introduced in Compose 1.7+. Use this for motion design tasks.

SKILL.md

5.0 KB, as published. Nobody here has run it

Jetpack Compose Animations

Instructions

Compose ships a layered animation toolkit. Pick the lowest-friction API that fits the motion.

1. Value Animations — animate*AsState

val elevation by animateDpAsState(
    targetValue = if (pressed) 1.dp else 6.dp,
    animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
    label = "cardElevation",
)

val tint by animateColorAsState(
    targetValue = if (isFav) Color.Red else MaterialTheme.colorScheme.outline,
    label = "favTint",
)

Use animate*AsState when a single property changes between two targets. Always pass label — it shows up in the Animation Preview in Android Studio.

2. Enter / Exit — AnimatedVisibility

AnimatedVisibility(
    visible = state.showBanner,
    enter = fadeIn() + expandVertically(),
    exit = fadeOut() + shrinkVertically(),
) {
    Banner(state.bannerText)
}

Combine specs with +. Prefer semantic specs (fadeIn, slideInVertically) over hand-crafted tweens.

3. Between-Content — AnimatedContent

AnimatedContent(
    targetState = state,
    label = "screen",
    transitionSpec = {
        (slideInHorizontally { it } + fadeIn()) togetherWith
        (slideOutHorizontally { -it } + fadeOut())
    },
) { s ->
    when (s) {
        is ArticlesUiState.Loading -> LoadingIndicator()
        is ArticlesUiState.Error   -> ErrorView(s.message)
        is ArticlesUiState.Loaded  -> ArticleList(s.articles)
    }
}

Always use SizeTransform (set via sizeTransform = SizeTransform(clip = false)) if the target content has a different size.

4. Coordinated — updateTransition

For multiple properties that should animate from the same state change:

val transition = updateTransition(targetState = expanded, label = "card")
val radius by transition.animateDp(label = "r") { if (it) 24.dp else 8.dp }
val alpha  by transition.animateFloat(label = "a") { if (it) 1f else 0.6f }
val height by transition.animateDp(label = "h") { if (it) 240.dp else 80.dp }

5. Infinite Animations

val infinite = rememberInfiniteTransition(label = "pulse")
val scale by infinite.animateFloat(
    initialValue = 1f, targetValue = 1.2f,
    animationSpec = infiniteRepeatable(
        animation = tween(600, easing = FastOutSlowInEasing),
        repeatMode = RepeatMode.Reverse,
    ),
    label = "scale",
)
Box(Modifier.scale(scale).size(24.dp).background(Color.Red, CircleShape))

6. Shared Element Transitions (Compose 1.7+)

SharedTransitionLayout {
    NavHost(navController, startDestination = ListRoute) {
        composable<ListRoute> { ArticleList(onOpen = { id -> navController.navigate(DetailRoute(id)) }, sharedScope = this@SharedTransitionLayout, animatedScope = this) }
        composable<DetailRoute> { args -> ArticleDetail(id = args.toRoute<DetailRoute>().id, sharedScope = this@SharedTransitionLayout, animatedScope = this) }
    }
}

@Composable
fun ArticleThumb(id: String, scope: SharedTransitionScope, animatedScope: AnimatedVisibilityScope) = with(scope) {
    AsyncImage(
        model = coverUrl,
        contentDescription = null,
        modifier = Modifier.sharedElement(
            state = rememberSharedContentState(key = "cover-$id"),
            animatedVisibilityScope = animatedScope,
        ).size(96.dp).clip(MaterialTheme.shapes.small),
    )
}

Use matching keys on both source and destination. Keep payloads identical (same aspect ratio, same clip).

7. Gestures + Physics

val offsetX = remember { Animatable(0f) }
val scope = rememberCoroutineScope()
Modifier.pointerInput(Unit) {
    detectHorizontalDragGestures(
        onHorizontalDrag = { _, dx -> scope.launch { offsetX.snapTo(offsetX.value + dx) } },
        onDragEnd        = { scope.launch { offsetX.animateTo(0f, spring(stiffness = Spring.StiffnessMedium)) } },
    )
}

Animatable is the right tool when gestures and animation share state.

8. Performance Notes

  • Prefer graphicsLayer { alpha = a; scaleX = s } over Modifier.alpha(a).scale(s) when animating — single layer, no recomposition.
  • Animate on Int/Dp/Float, not Modifier swaps.
  • Respect reduced motion: read AccessibilityManager.isReducedMotionEnabled (via LocalAccessibilityManager) and fall back to instant snap.

Checklist

  • Every animation has a descriptive label for the Animation Preview.
  • AnimatedVisibility is used for show/hide, AnimatedContent for content swaps.
  • Multi-property animations use a single updateTransition.
  • Shared element transitions key source and destination with the same string.
  • graphicsLayer is preferred over stacking visual modifiers for animated values.
  • Reduced-motion setting is respected for non-essential motion.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.