Shared Elements

AnimatedContent and Shared Element Transitions

AnimatedContent(targetState) animates between two versions of some content, by default fading the old one out while the new one fades in and animating the size between them. BookNest uses it twice: the button's label switches between "Add to cart" and "In your cart", and the cart badge counts up and down:

ui/nav/BookNestNav.kt: an animated badgeKotlin
            BadgedBox(badge = { Badge { AnimatedContent(cartCount) { Text("$it") } } }) {

Use the lambda's parameter, not cartCount: during the transition an old and a new copy exist, and each must draw its own value. transitionSpec can slide numbers up or down instead.

A shared element transition makes one element appear to travel between screens, like the cover growing from a catalog row into the detail header. Wrap the NavHost in SharedTransitionLayout, give both covers the same key, and pass the AnimatedVisibilityScope that each composable { } destination provides:

ui/BookScreens.kt: the same key on both screens (two excerpts)Kotlin
        CoilCover(book, Modifier.sharedElement(rememberSharedContentState("cover-${book.id}"),
          animatedVisibilityScope = visibility))
...
    CoilCover(book, Modifier.sharedElement(rememberSharedContentState("cover-${book.id}"),
      animatedVisibilityScope = visibility).size(160.dp, 240.dp))

During navigation Compose 234 measures both elements, draws the shared one in an overlay above both screens, and animates its bounds from the 80 by 120 dp row to the 160 by 240 dp header. BookNest still opts in with @OptIn(ExperimentalSharedTransitionApi::class). Keys must be unique per screen, hence the book ID; sharedBounds is the variant for two different-looking elements that share a container.