SharedTransitionLayout

Functions summary

Unit

SharedTransitionLayout creates a layout and a SharedTransitionScope for the child layouts in content.

Cmn

Functions

SharedTransitionLayout

@Composable
fun SharedTransitionLayout(
    modifier: Modifier = Modifier,
    content: @Composable SharedTransitionScope.() -> Unit
): Unit

SharedTransitionLayout creates a layout and a SharedTransitionScope for the child layouts in content. Any child (direct or indirect) of the SharedTransitionLayout can use the receiver scope SharedTransitionScope to create shared element or shared bounds transitions.

Note: SharedTransitionLayout creates a new Layout. For use cases where it's preferable to not introduce a new layout between content and the parent layout, consider using SharedTransitionScope instead.

Below is an example of using SharedTransitionLayout to create a shared element transition and a shared bounds transition at the same time. Please see the API docs for SharedTransitionScope.sharedElement and SharedTransitionScope.sharedBounds for more simplified examples of using these APIs separately.

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.SharedTransitionScope.SharedContentState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.material.icons.outlined.Share
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.sp

// This is the Image that we will add shared element modifier on. It's important to make sure
// modifiers that are not shared between the two shared elements (such as size modifiers if
// the size changes) are the parents (i.e. on the left side) of Modifier.sharedElement.
// Meanwhile, the modifiers that are shared between the shared elements (e.g. Modifier.clip
// in this case) are on the right side of the Modifier.sharedElement.
@Composable
fun Cat(modifier: Modifier = Modifier) {
    Image(
        painterResource(id = R.drawable.yt_profile),
        contentDescription = "cute cat",
        contentScale = ContentScale.FillHeight,
        modifier = modifier.clip(shape = RoundedCornerShape(10)),
    )
}

// Shared element key is of type `Any`, which means it can be id, string, etc. The only
// requirement for the key is that it should be the same for shared elements that you intend
// to match. Here we use the image resource id as the key.
val sharedElementKey = R.drawable.yt_profile
var showLargeImage by remember { mutableStateOf(true) }

// First, we need to create a SharedTransitionLayout, this Layout will provide the coordinator
// space for shared element position animation, as well as an overlay for shared elements to
// render in. Children content in this Layout will be able to create shared element transition
// using the receiver scope: SharedTransitionScope
SharedTransitionLayout(
    Modifier.clickable { showLargeImage = !showLargeImage }.fillMaxSize().padding(10.dp)
) {
    // In the SharedTransitionLayout, we will be able to access the receiver scope (i.e.
    // SharedTransitionScope) in order to create shared element transition.
    AnimatedContent(targetState = showLargeImage) { showLargeImageMode ->
        if (showLargeImageMode) {
            Cat(
                Modifier.fillMaxSize()
                    .aspectRatio(1f)
                    // Creating a shared element. Note that this modifier is *after*
                    // the size modifier and aspectRatio modifier, because those size specs
                    // are not shared between the two shared elements.
                    .sharedElement(
                        rememberSharedContentState(sharedElementKey),
                        // Using the AnimatedVisibilityScope from the AnimatedContent
                        // defined above.
                        this@AnimatedContent,
                    )
            )
            Text(
                "Cute Cat YT",
                fontSize = 40.sp,
                color = Color.Blue,
                // Prefer Modifier.sharedBounds for text, unless the texts in both initial
                // content and target content are exactly the same (i.e. same
                // size/font/color)
                modifier =
                    Modifier.fillMaxWidth()
                        // IMPORTANT: Prefer using wrapContentWidth/wrapContentSize over
                        // textAlign
                        // for shared text transition. This allows the layout system sees actual
                        // position and size of the text to facilitate bounds animation.
                        .wrapContentWidth(Alignment.CenterHorizontally)
                        .sharedBounds(
                            rememberSharedContentState(key = "text"),
                            this@AnimatedContent,
                        ),
            )
        } else {
            Column {
                Row(verticalAlignment = Alignment.CenterVertically) {
                    Cat(
                        Modifier.size(100.dp)
                            // Creating another shared element with the same key.
                            // Note that this modifier is *after* the size modifier,
                            // The size changes between these two shared elements, i.e. the size
                            // is not shared between the two shared elements.
                            .sharedElement(
                                rememberSharedContentState(sharedElementKey),
                                this@AnimatedContent,
                            )
                    )
                    Text(
                        "Cute Cat YT",
                        // Change text color & size
                        fontSize = 20.sp,
                        color = Color.DarkGray,
                        // Prefer Modifier.sharedBounds for text, unless the texts in both
                        // initial content and target content are exactly the same (i.e. same
                        // size/font/color)
                        modifier =
                            Modifier
                                // The modifier that is not a part of the shared content, but
                                // rather
                                // for positioning and sizes should be on the *left* side of
                                // sharedBounds/sharedElement.
                                .padding(start = 20.dp)
                                .sharedBounds(
                                    // Here we use a string-based key, in contrast to the key
                                    // above.
                                    rememberSharedContentState(key = "text"),
                                    this@AnimatedContent,
                                ),
                    )
                }
                Box(
                    Modifier.fillMaxWidth()
                        .height(100.dp)
                        .background(Color(0xffffcc5c), RoundedCornerShape(5.dp))
                )
                Box(
                    Modifier.fillMaxWidth()
                        .height(100.dp)
                        .background(Color(0xff2a9d84), RoundedCornerShape(5.dp))
                )
            }
        }
    }
}
Parameters
modifier: Modifier = Modifier

Modifiers to be applied to the layout.

content: @Composable SharedTransitionScope.() -> Unit

The children composable to be laid out.