If you’ve ever needed to use an existing C library in a Kotlin project, you’ve probably run into the gap between modern Kotlin and low-level native code. That’s where Kotlin Native CInterop comes in. This guide breaks it down in a practical way. No fluff, just what you need to understand how it works and...
When you start working with animations in Jetpack Compose, the spring() API feels intuitive at first—until you notice something odd: animations don’t seem to fully stop. They get very, very close to the target value… but technically never reach it.
That’s where visibilityThreshold quietly does some of the most important work.
This article walks you through what it is, why it matters, and how to use it correctly across different data types like Dp, Float, and Offset. Along the way, we’ll build real composable examples you can run and experiment with.
Why Spring Animations Need a Stopping Condition
Spring animations simulate real physics, Instead of moving linearly from point A to point B, they behave like a spring:
They move toward the target
Overshoot (depending on damping)
Oscillate
Gradually settle
From a math perspective, they never fully stop — they just get closer over time.
In UI, that’s not useful. We need a clear stopping point. Compose handles this using a threshold.
What is visibilityThreshold?
visibilityThreshold defines the minimum difference between the current animated value and the target value at which the animation is considered finished.
In simple terms:
If the remaining distance is smaller than the threshold → animation stops
If not → animation continues
How It Works for Different Types
Different types need different thresholds because “closeness” depends on the unit.
Animations in the old View system were a lot of ceremony. You’d set up an ObjectAnimator, attach a listener, call start(), remember to cancel on detach, and hope nothing leaked. For something as simple as fading a view, it felt disproportionate. Compose takes a different approach. Instead of imperative animation commands, you describe what you...
If you’ve been building UI with Jetpack Compose for more than a week, you already know the drill. You write a clean Composable — maybe a PrimaryButton, or a UserProfileCard — and then you immediately have to write something like this below it:
But think about how many Composables you build across a full project. Ten screens, each with five or six components — that’s fifty-plus preview functions you’re hand-typing (or copy-pasting and then refactoring). The mental cost isn’t the typing itself; it’s the context switching. You just nailed your component logic, and now you have to stop, copy the function name, jump below, paste, rename, and restructure. It breaks the creative flow completely.
The good news: Android Studio already has a built-in system designed exactly for this. You just need to set it up once.
Quick answer for the impatient: Go to Settings → Editor → Live Templates, create a new template with the abbreviation prev (or use the one already available in Android Studio), and paste the template code from the next section. That’s it. The rest of this post goes deeper into why and when to use each approach.
Live Templates — The Fastest Fix You’re Not Using
Live Templates are one of Android Studio’s most underused features. They’re essentially smart text snippets that expand when you type a short abbreviation and press Tab. IntelliJ has had them for years — Android Studio inherits them from the same codebase. Kotlin developers who come from other editors sometimes have no idea this exists.
For @Preview specifically, the goal is: you type prev, hit Tab, and the entire preview scaffold appears with your cursor already positioned inside it, ready to fill in any needed parameters.
Setting Up Your First Preview Live Template
Here’s the exact step-by-step process — no skipping ahead:
1. Open Settings
On macOS press ⌘ ,.
On Windows/Linux go to File → Settings.
In the search bar, type “Live Templates” to jump straight to it.
2. Create a new Template Group
In the Live Templates panel, click the + button on the right → select Template Group→ name it something like Compose. This keeps things organized and separate from Android Studio’s built-in templates.
3. Add a new Live Template
With your new Compose group selected, click + again → this time select Live Template.
4. Fill in the abbreviation and description
Set Abbreviation to prev and give it a Description like “Compose @Preview function”. The description shows up in autocomplete hints, so keep it readable.
5. Paste the template text
This is the core part. Paste the code below exactly as shown — the $VARIABLE$ syntax is how Android Studio knows where to place your cursor and what to ask you to fill in.
The $COMPOSABLE_NAME$ variable is smart — when you tab into the template, Android Studio highlights every occurrence at once. Type the name once and it fills in both places simultaneously (the function name and the call inside it). The $END$ marker tells the editor where to park your cursor after you’re done naming — right inside the parentheses where you’d add parameters.
If you want, you can wrap it in your existing app theme or a Material theme like this:
At the bottom of the template editor, click Define and check Kotlin. Without this step, the template won’t activate inside .kt files.
7. Hit OK and test it
Open any Kotlin file, type prev, and press Tab. The scaffold should appear.
What It Looks Like In Practice
Before & After
Kotlin
// You write this composable first:@ComposablefunUserProfileCard( name: String, avatarUrl: String, isOnline: Boolean) {// ... your component logic}// Then type "prev" + Tab, type "UserProfileCard", Tab again:@Preview(showBackground = true)@ComposablefunUserProfileCardPreview() {UserProfileCard(// ← cursor lands here, ready for sample data )}
One thing to remember: The template inserts the composable name as a plain text call. If your Composable has required parameters, Android Studio won’t auto-fill them — you’ll need to add sample data yourself. That’s expected and by design; previews should use meaningful placeholder values, not auto-generated garbage.
Multi-Variant Previews: Light, Dark, and Different Screen Sizes
A single light-mode preview is fine for early development. But before you ship anything, you want to see your component in at least two states: light theme and dark theme. You might also want to check it on a compact phone screen vs a larger device. Doing this manually every time is even more tedious than writing a basic preview.
There are two solid ways to handle this in Compose. The cleaner of the two is a custom annotation that stacks multiple @Preview declarations — this keeps your composable files lean and consistent across your entire project.
Approach A: Custom Multi-Preview Annotation
Create a single annotation class in a shared file (something like PreviewAnnotations.kt in your UI module’s utils package):
Between these two approaches, the custom annotation (Approach A) is better for teams and larger projects. It lives in source control, everyone uses the same preview config automatically, and updating it once updates every preview across the codebase. Live Templates are per-developer and per-machine — great for solo work, less ideal for shared codebases.
Note: Instead of defining custom @DevicePreviews like above, you can use the built-in@PreviewScreenSizes.
File Templates: Scaffold Both the Composable and Preview at Once
Live Templates solve the in-file boilerplate problem. But what if your workflow always starts with creating a new file? If you find yourself doing File → New → Kotlin File/Class and then manually typing the @Composable and @Preview blocks from scratch, File Templates take this even further.
A File Template is a pre-defined structure that Android Studio uses when you create a new file through the right-click menu. You can define your own and make “New Composable File” a real option.
1. Open Settings → File and Code Templates
Navigate to Editor → File and Code Templates. You’ll see the default list of templates on the left (Kotlin File, Interface, Class, etc.).
2. Click + to create a new template
Name it something like Composable, set the extension to kt.
3. Paste the template body
Use the $NAME variable — Android Studio prompts the user to fill this in when they create the file.
After saving this, right-clicking any package in your project tree will show New → Composable in the menu. You type the component name once, and you get a file with proper package declaration, imports, a blank composable, and its preview — all ready to go.
Which Approach Should You Actually Use?
The honest answer: it depends on your context. Here’s a clear breakdown to help you decide without overthinking it.
For most individual developers: start with a Live Template. It takes five minutes, pays off immediately, and you don’t need to touch it again. If you’re working on a team or a long-lived codebase, invest the extra ten minutes to set up a custom @DevicePreviews annotation and commit it to the repo. That way the entire team benefits without any individual setup.
Conclusion
The friction of writing @Preview boilerplate is real, but it’s entirely self-imposed. Android Studio has the tools to make this near-instant — you just need to spend fifteen minutes setting them up once. A Live Template handles the basic case in two keystrokes. A custom annotation handles multi-variant previews for teams. File Templates handle the new-file workflow.
Pick the one that fits your current workflow and set it up today. The next time you build a Composable, you’ll feel the difference immediately.
Animations are one of those things that feel easy until you actually try to wire them into a real screen. You start with a simple fade or size change, and suddenly you’re juggling state, re-composition, and timing issues that don’t behave the way you expected.
I ran into this while building a product listing screen. Small interactions like expanding cards, animating filters, and handling loading states quickly became messy. That’s when the Jetpack Compose Animation System started to make sense — not as a set of APIs, but as a model tied directly to state.
This post breaks that down in a practical way.
What the Jetpack Compose Animation System Actually Is
The core idea is straightforward:
Your UI depends on state, and animations happen when that state changes.
You don’t trigger animations manually. You describe what the UI should look like for a given state, and Compose handles the transition.
Instead of writing something like “start animation on click”, you write:
if expanded → height = 200dp
if collapsed → height = 100dp
When the state changes, Compose animates between those values.
Understanding this mental model will make everything else click. In Compose, your UI is a function of state:
UI = f(state) — When state changes, Compose re-renders the UI. Animations are just a smooth interpolation between two states over time. You don’t “run” an animation — you change state and tell Compose how to animate the transition.
The animation system in Compose has three layers, and it’s worth knowing which layer you’re working at:
Layer 1 — High-level APIs:AnimatedVisibility, AnimatedContent, Crossfade. These handle the most common cases with zero configuration needed.
Layer 2 — Value-based APIs:animate*AsState, updateTransition, InfiniteTransition. These animate specific values (Float, Dp, Color, etc.) that you then apply in your composables.
Layer 3 — Low-level APIs:Animatable, coroutine-based. Full manual control for complex sequencing, interruptions, or physics-based motion.
The golden rule: start at the highest level that solves your problem. Only go deeper when you genuinely need more control. Most production animations live happily in layers 1 and 2.
The Core Building Blocks
Before writing any animations, it helps to understand the main APIs you’ll actually use:
1. animate*AsState
For simple, one-off animations tied to a single value.
2. updateTransition
For animating multiple values based on the same state.
3. AnimatedVisibility
For showing and hiding composables with animation.
4. AnimatedContent
For switching between UI states.
5. rememberInfiniteTransition
For looping animations.
You don’t need all of them at once. Most real screens use 1–2 of these consistently.
Why This Model Works Well
Once you lean into this approach, a few things improve right away:
No need to manage animation lifecycle
No manual cancellation logic
UI stays consistent with state
Less glue code
This becomes especially useful when multiple properties change together. You don’t coordinate them manually. You just describe the end result.
Your First Real Animation
Let’s build something practical: a card that expands when clicked.
The Jetpack Compose Animation System feels strange at first because it flips the mental model. You’re not telling the UI how to animate. You’re describing how it should look in different states.
Once that clicks, animations become predictable.
Start small:
Animate size
Then color
Then combine them
After a few screens, you’ll stop thinking about “animations” entirely and just think in terms of state transitions.
Most Android performance improvements land as a framework update or a new API. This one is different. Starting with Android 15, Google added support for a 16 KB page size on ARM64 devices — and with Android 16, it’s becoming a hard requirement for apps that target new hardware. If you haven’t looked into this yet, now...
If you’ve worked with Android long enough, you already know this: emulator performance isn’t just about speed, it’s about consistency. A fast emulator that behaves unpredictably is worse than a slightly slower one that’s stable. This guide focuses on Android Emulator Settings that hold up in real-world development. Not just for solo projects, but for...
Jetpack Compose continues to evolve, and one of the most interesting updates is the new Ripple API. If you’ve been building modern Android UIs, you’ve probably used ripple effects to give users visual feedback when they tap on buttons, cards, or other interactive elements. That subtle wave animation plays a big role in making interactions...
AI-powered coding assistants have completely changed how Android developers write code. Features like Gemini in Android Studio read your project files, understand your codebase structure, and suggest intelligent completions. That’s incredibly helpful — but it also raises an important question: What exactly is the AI reading? The answer is often “more than you think.” Configuration files, API...
If you’ve been in the mobile and cross-platform world lately, you’ve probably heard a lot about Compose Multiplatform (CMP). It’s one of the fastest-growing ways to build apps that run on Android, iOS and the Web using a single shared UI approach. But what exactly is CMP? And why are developers increasingly choosing it over...