Mastering Shared Element Transitions in Jetpack Compose: From Basics to Advanced Tricks
Explore the complete guide to implementing shared element transitions in Jetpack Compose, covering essential APIs like SharedTransitionLayout, Modifier.sharedElement, and Modifier.sharedBounds, with detailed code examples, common pitfalls, container transforms, resize modes, and practical solutions for smooth, hero‑style animations across Android screens.
Clear Introduction to Compose Shared Element Transitions
Shared element transitions smoothly connect identical content across different screens, improving navigation continuity. In Compose they rely on three main APIs:
SharedTransitionLayout,
Modifier.sharedElement(), and
Modifier.sharedBounds().
Because the related APIs are still in beta, remember to add the dependency implementation("androidx.compose.animation:animation:1.7.0-beta06") .
SharedTransitionLayout and sharedElement Modifier
Both pages must contain the same content; otherwise there is nothing to transition.
@Composable
fun BasicSharedElementDemo() {
var showDetails by remember { mutableStateOf(false) }
SharedTransitionLayout {
if (showDetails) {
DetailsScreen(onBack = { showDetails = false })
} else {
MainScreen(onShowDetails = { showDetails = true })
}
}
}To mark shared elements, use
Modifier.sharedElement()with a
SharedContentStatecreated by
rememberSharedContentState(key = "unique_key"). The modifier also requires an
AnimatedVisibilityScopewhich is usually supplied by
AnimatedVisibilityor
AnimatedContent.
SharedBounds Modifier
Modifier.sharedBounds()works like
sharedElement()but is intended for container‑level transitions where the visual content differs while the shared area remains the same.
@Composable
private fun SharedTransitionScope.MainScreen(onShowDetails: () -> Unit, animatedVisibilityScope: AnimatedVisibilityScope, modifier: Modifier = Modifier) {
Box {
Row(
modifier = Modifier
.padding(16.dp)
.sharedBounds(
sharedContentState = rememberSharedContentState(key = "bounds"),
animatedVisibilityScope = animatedVisibilityScope
)
) {
Image(
painter = painterResource(id = R.drawable.avatar),
contentDescription = null,
modifier = Modifier
.size(100.dp)
.sharedElement(
state = rememberSharedContentState(key = "image"),
animatedVisibilityScope = animatedVisibilityScope
)
)
Text(
text = "bqliang",
fontSize = 21.sp,
modifier = Modifier.sharedElement(
state = rememberSharedContentState(key = "title"),
animatedVisibilityScope = animatedVisibilityScope
)
)
}
}
}Using
sharedBounds()solves two common problems: container background transitions and abrupt text size changes. The modifier provides
enterand
exittransitions (default fade‑in/out) and a
resizeModethat controls how content scales. The default
ScaleToBounds(ContentScale.FillWidth, Center)can produce odd animations; switching to
RemeasureToBoundsforces the content to be re‑measured to fill the evolving bounds, yielding smoother results.
@Composable
fun BasicSharedElementDemo() {
var showDetails by remember { mutableStateOf(false) }
SharedTransitionLayout {
AnimatedContent(targetState = showDetails) { inDetails ->
if (inDetails) {
DetailsScreen(onBack = { showDetails = false }, animatedVisibilityScope = this@AnimatedContent)
} else {
MainScreen(onShowDetails = { showDetails = true }, animatedVisibilityScope = this@AnimatedContent)
}
}
}
}When the default
resizeModestretches content only horizontally, the start and end elements may not align. Using
RemeasureToBoundsor
ScaleToBounds(ContentScale.FillBounds, Center)changes the scaling behavior. For text,
sharedBounds()keeps the original font size while fading, avoiding sudden size jumps.
Summary
Shared element and shared bounds modifiers must be used inside a
SharedTransitionScopeand typically together with
AnimatedVisibilityor
AnimatedContent.
sharedElement()is for identical content;
sharedBounds()is for container‑level transitions.
During transition,
sharedElement()renders only the target content, while
sharedBounds()renders both start and target, applying default fade‑in/out.
sharedBounds()can be customized with
enter,
exit, and
resizeModeto achieve the desired animation.
AndroidPub
Senior Android Developer & Interviewer, regularly sharing original tech articles, learning resources, and practical interview guides. Welcome to follow and contribute!
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.