Compose में शेयर किए गए एलिमेंट के ट्रांज़िशन

शेयर किए गए एलिमेंट के ट्रांज़िशन, एक जैसे कॉन्टेंट वाले कंपोज़ेबल के बीच आसानी से ट्रांज़िशन करने का एक तरीका है. इनका इस्तेमाल अक्सर नेविगेशन के लिए किया जाता है. इससे, उपयोगकर्ता के एक स्क्रीन से दूसरी स्क्रीन पर जाने के दौरान, आपको अलग-अलग स्क्रीन को विज़ुअली कनेक्ट करने की सुविधा मिलती है.

उदाहरण के लिए, इस वीडियो में आपको दिखेगा कि स्नैक की इमेज और टाइटल, लिस्टिंग पेज से जानकारी वाले पेज पर शेयर किए गए हैं.

पहली इमेज. Jetsnack में शेयर किए गए एलिमेंट का डेमो.

Compose में, कुछ हाई लेवल एपीआई होते हैं. इनकी मदद से, शेयर किए गए एलिमेंट बनाए जा सकते हैं:

  • SharedTransitionLayout: शेयर किए गए एलिमेंट के ट्रांज़िशन लागू करने के लिए, सबसे बाहरी लेआउट ज़रूरी है. यह SharedTransitionScope उपलब्ध कराता है. शेयर किए गए एलिमेंट मॉडिफ़ायर का इस्तेमाल करने के लिए, कंपोज़ेबल को SharedTransitionScope में होना चाहिए.
  • Modifier.sharedElement(): यह मॉडिफ़ायर, SharedTransitionScope को फ़्लैग करता है. यह कंपोज़ेबल, किसी दूसरे कंपोज़ेबल से मैच होना चाहिए.
  • Modifier.sharedBounds(): यह मॉडिफ़ायर, SharedTransitionScope को यह सूचना देता है कि इस कंपोज़ेबल की सीमाओं का इस्तेमाल, कंटेनर की सीमाओं के तौर पर किया जाना चाहिए. ऐसा इसलिए, ताकि ट्रांज़िशन हो सके. sharedElement() के उलट, sharedBounds() को ऐसे कॉन्टेंट के लिए डिज़ाइन किया गया है जो देखने में अलग हो.

Compose में शेयर किए गए एलिमेंट बनाते समय, यह जानना ज़रूरी है कि वे ओवरले और क्लिपिंग के साथ कैसे काम करते हैं. इस ज़रूरी विषय के बारे में ज़्यादा जानने के लिए, क्लिपिंग और ओवरले सेक्शन देखें.

बुनियादी इस्तेमाल

इस सेक्शन में, नीचे दिया गया ट्रांज़िशन बनाया जाएगा. इसमें, "सूची" वाले छोटे आइटम से, ज़्यादा जानकारी वाले बड़े आइटम पर ट्रांज़िशन किया जाएगा:

दूसरी इमेज. दो कंपोज़ेबल के बीच शेयर किए गए एलिमेंट के ट्रांज़िशन का सामान्य उदाहरण.

Modifier.sharedElement() का सबसे अच्छा तरीका यह है कि इसे AnimatedContent, AnimatedVisibility या NavHost के साथ इस्तेमाल किया जाए. इससे कंपोज़ेबल के बीच ट्रांज़िशन अपने-आप मैनेज हो जाता है.

शुरुआत में, एक बुनियादी AnimatedContent होता है, जिसमें MainContent होता है. साथ ही, शेयर किए गए एलिमेंट जोड़ने से पहले DetailsContent कंपोज़ेबल होता है:

तीसरी इमेज. शेयर किए गए किसी भी एलिमेंट के ट्रांज़िशन के बिना AnimatedContent शुरू हो रहा है.

  1. शेयर किए गए एलिमेंट को दो लेआउट के बीच ऐनिमेट करने के लिए, AnimatedContent कंपोज़ेबल को SharedTransitionLayout से घेरें. SharedTransitionLayout और AnimatedContent से मिले स्कोप, MainContent और DetailsContent को पास किए जाते हैं:

    var showDetails by remember {
        mutableStateOf(false)
    }
    SharedTransitionLayout {
        AnimatedContent(
            showDetails,
            label = "basic_transition"
        ) { targetState ->
            if (!targetState) {
                MainContent(
                    onShowDetails = {
                        showDetails = true
                    },
                    animatedVisibilityScope = this@AnimatedContent,
                    sharedTransitionScope = this@SharedTransitionLayout
                )
            } else {
                DetailsContent(
                    onBack = {
                        showDetails = false
                    },
                    animatedVisibilityScope = this@AnimatedContent,
                    sharedTransitionScope = this@SharedTransitionLayout
                )
            }
        }
    }

  2. मिलते-जुलते दो कंपोज़ेबल पर, कंपोज़ेबल मॉडिफ़ायर चेन में Modifier.sharedElement() जोड़ें. एक SharedContentState ऑब्जेक्ट बनाएं और उसे rememberSharedContentState() की मदद से याद रखें. SharedContentState ऑब्जेक्ट, यूनीक कुंजी को सेव कर रहा है. इससे यह तय होता है कि कौनसे एलिमेंट शेयर किए जाएंगे. कॉन्टेंट की पहचान करने के लिए, एक यूनीक कुंजी दें. साथ ही, आइटम को याद रखने के लिए rememberSharedContentState() का इस्तेमाल करें. AnimatedContentScope को मॉडिफ़ायर में पास किया जाता है. इसका इस्तेमाल ऐनिमेशन को कोऑर्डिनेट करने के लिए किया जाता है.

    @Composable
    private fun MainContent(
        onShowDetails: () -> Unit,
        modifier: Modifier = Modifier,
        sharedTransitionScope: SharedTransitionScope,
        animatedVisibilityScope: AnimatedVisibilityScope
    ) {
        Row(
            // ...
        ) {
            with(sharedTransitionScope) {
                Image(
                    painter = painterResource(id = R.drawable.cupcake),
                    contentDescription = "Cupcake",
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(key = "image"),
                            animatedVisibilityScope = animatedVisibilityScope
                        )
                        .size(100.dp)
                        .clip(CircleShape),
                    contentScale = ContentScale.Crop
                )
                // ...
            }
        }
    }
    
    @Composable
    private fun DetailsContent(
        modifier: Modifier = Modifier,
        onBack: () -> Unit,
        sharedTransitionScope: SharedTransitionScope,
        animatedVisibilityScope: AnimatedVisibilityScope
    ) {
        Column(
            // ...
        ) {
            with(sharedTransitionScope) {
                Image(
                    painter = painterResource(id = R.drawable.cupcake),
                    contentDescription = "Cupcake",
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(key = "image"),
                            animatedVisibilityScope = animatedVisibilityScope
                        )
                        .size(200.dp)
                        .clip(CircleShape),
                    contentScale = ContentScale.Crop
                )
                // ...
            }
        }
    }

शेयर किए गए एलिमेंट का मिलान हुआ है या नहीं, इस बारे में जानकारी पाने के लिए, rememberSharedContentState() को किसी वैरिएबल में निकालें और isMatchFound को क्वेरी करें.

इससे यह ऐनिमेशन अपने-आप बन जाता है:

चौथी इमेज. दो कंपोज़ेबल के बीच शेयर किए गए एलिमेंट के ट्रांज़िशन का सामान्य उदाहरण.

आपको दिख सकता है कि पूरे कंटेनर के बैकग्राउंड का रंग और साइज़ अब भी डिफ़ॉल्ट AnimatedContent सेटिंग का इस्तेमाल कर रहा है.

शेयर किए गए बाउंड्री बॉक्स बनाम शेयर किया गया एलिमेंट

Modifier.sharedBounds(), Modifier.sharedElement() से मिलता-जुलता है. हालांकि, मॉडिफ़ायर इन तरीकों से अलग होते हैं:

  • sharedBounds() का इस्तेमाल ऐसे कॉन्टेंट के लिए किया जाता है जो देखने में अलग हो, लेकिन राज्यों के बीच एक ही इलाके को शेयर करता हो. वहीं, sharedElement() का इस्तेमाल ऐसे कॉन्टेंट के लिए किया जाता है जो एक जैसा हो.
  • sharedBounds() का इस्तेमाल करने पर, स्क्रीन पर आने और जाने वाला कॉन्टेंट, दो स्थितियों के बीच ट्रांज़िशन के दौरान दिखता है. वहीं, sharedElement() का इस्तेमाल करने पर, सिर्फ़ टारगेट कॉन्टेंट को ट्रांसफ़ॉर्मिंग बाउंड्री में रेंडर किया जाता है. Modifier.sharedBounds() में enter और exit पैरामीटर होते हैं. इनकी मदद से यह तय किया जाता है कि कॉन्टेंट को कैसे ट्रांज़िशन करना है. यह AnimatedContent के काम करने के तरीके जैसा ही है.
  • sharedBounds() का सबसे ज़्यादा इस्तेमाल कंटेनर ट्रांसफ़ॉर्म पैटर्न में किया जाता है. वहीं, sharedElement() का इस्तेमाल हीरो ट्रांज़िशन में किया जाता है.
  • Text कंपोज़ेबल का इस्तेमाल करते समय, फ़ॉन्ट में बदलाव करने के लिए sharedBounds() का इस्तेमाल करना बेहतर होता है. जैसे, इटैलिक और बोल्ड के बीच ट्रांज़िशन करना या रंग बदलना.

पिछले उदाहरण में, दो अलग-अलग स्थितियों में Row और Column में Modifier.sharedBounds() जोड़ने से, हमें दोनों की सीमाओं को शेयर करने और ट्रांज़िशन ऐनिमेशन दिखाने की अनुमति मिलेगी. इससे दोनों एक-दूसरे के बीच बढ़ पाएंगे:

@Composable
private fun MainContent(
    onShowDetails: () -> Unit,
    modifier: Modifier = Modifier,
    sharedTransitionScope: SharedTransitionScope,
    animatedVisibilityScope: AnimatedVisibilityScope
) {
    with(sharedTransitionScope) {
        Row(
            modifier = Modifier
                .padding(8.dp)
                .sharedBounds(
                    rememberSharedContentState(key = "bounds"),
                    animatedVisibilityScope = animatedVisibilityScope,
                    enter = fadeIn(),
                    exit = fadeOut(),
                    resizeMode = SharedTransitionScope.ResizeMode.ScaleToBounds()
                )
                // ...
        ) {
            // ...
        }
    }
}

@Composable
private fun DetailsContent(
    modifier: Modifier = Modifier,
    onBack: () -> Unit,
    sharedTransitionScope: SharedTransitionScope,
    animatedVisibilityScope: AnimatedVisibilityScope
) {
    with(sharedTransitionScope) {
        Column(
            modifier = Modifier
                .padding(top = 200.dp, start = 16.dp, end = 16.dp)
                .sharedBounds(
                    rememberSharedContentState(key = "bounds"),
                    animatedVisibilityScope = animatedVisibilityScope,
                    enter = fadeIn(),
                    exit = fadeOut(),
                    resizeMode = SharedTransitionScope.ResizeMode.ScaleToBounds()
                )
                // ...

        ) {
            // ...
        }
    }
}

पांचवीं इमेज. दो कंपोज़ेबल के बीच शेयर की गई सीमाएं.

स्कोप के बारे में जानकारी

Modifier.sharedElement() का इस्तेमाल करने के लिए, कंपोज़ेबल को SharedTransitionScope में होना चाहिए. SharedTransitionLayout कंपोज़ेबल, SharedTransitionScope उपलब्ध कराता है. पक्का करें कि आपने इसे यूज़र इंटरफ़ेस (यूआई) हैरारकी के उसी टॉप-लेवल पॉइंट पर रखा हो जिसमें वे एलिमेंट शामिल हैं जिन्हें आपको शेयर करना है.

आम तौर पर, कंपोज़ेबल को AnimatedVisibilityScope के अंदर भी रखा जाना चाहिए. आम तौर पर, यह सुविधा AnimatedContent का इस्तेमाल करके कंपोज़ेबल के बीच स्विच करने या सीधे AnimatedVisibility का इस्तेमाल करने पर मिलती है. इसके अलावा, NavHost कंपोज़ेबल फ़ंक्शन का इस्तेमाल करके भी यह सुविधा मिलती है. हालांकि, ऐसा तब तक होता है, जब तक विज़िबिलिटी को मैन्युअल तरीके से मैनेज न किया जाए. एक से ज़्यादा स्कोप का इस्तेमाल करने के लिए, अपने ज़रूरी स्कोप को CompositionLocal में सेव करें, Kotlin में कॉन्टेक्स्ट रिसीवर का इस्तेमाल करें या अपने फ़ंक्शन में स्कोप को पैरामीटर के तौर पर पास करें.

CompositionLocals का इस्तेमाल तब करें, जब आपको कई स्कोप ट्रैक करने हों या हैरारकी बहुत ज़्यादा नेस्ट की गई हो. CompositionLocal की मदद से, सेव करने और इस्तेमाल करने के लिए सटीक स्कोप चुने जा सकते हैं. दूसरी ओर, कॉन्टेक्स्ट रिसीवर का इस्तेमाल करने पर, आपकी हाइरार्की में मौजूद अन्य लेआउट, दिए गए स्कोप को गलती से बदल सकते हैं. उदाहरण के लिए, अगर आपके पास कई नेस्ट किए गए AnimatedContent हैं, तो स्कोप बदले जा सकते हैं.

val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null }
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null }

@Composable
private fun SharedElementScope_CompositionLocal() {
    // An example of how to use composition locals to pass around the shared transition scope, far down your UI tree.
    // ...
    SharedTransitionLayout {
        CompositionLocalProvider(
            LocalSharedTransitionScope provides this
        ) {
            // This could also be your top-level NavHost as this provides an AnimatedContentScope
            AnimatedContent(state, label = "Top level AnimatedContent") { targetState ->
                CompositionLocalProvider(LocalNavAnimatedVisibilityScope provides this) {
                    // Now we can access the scopes in any nested composables as follows:
                    val sharedTransitionScope = LocalSharedTransitionScope.current
                        ?: throw IllegalStateException("No SharedElementScope found")
                    val animatedVisibilityScope = LocalNavAnimatedVisibilityScope.current
                        ?: throw IllegalStateException("No AnimatedVisibility found")
                }
                // ...
            }
        }
    }
}

इसके अलावा, अगर आपकी हैरारकी में ज़्यादा लेवल नहीं हैं, तो स्कोप को पैरामीटर के तौर पर पास किया जा सकता है:

@Composable
fun MainContent(
    animatedVisibilityScope: AnimatedVisibilityScope,
    sharedTransitionScope: SharedTransitionScope
) {
}

@Composable
fun Details(
    animatedVisibilityScope: AnimatedVisibilityScope,
    sharedTransitionScope: SharedTransitionScope
) {
}

AnimatedVisibility के साथ शेयर किए गए एलिमेंट

पिछले उदाहरणों में, AnimatedContent के साथ शेयर किए गए एलिमेंट इस्तेमाल करने का तरीका बताया गया था. हालांकि, शेयर किए गए एलिमेंट AnimatedVisibility के साथ भी काम करते हैं.

उदाहरण के लिए, लेज़ी ग्रिड के इस उदाहरण में, हर एलिमेंट को AnimatedVisibility में रैप किया गया है. किसी आइटम पर क्लिक करने पर, कॉन्टेंट को यूज़र इंटरफ़ेस (यूआई) से खींचकर, डायलॉग जैसे कॉम्पोनेंट में ले जाया जाता है.

var selectedSnack by remember { mutableStateOf<Snack?>(null) }

SharedTransitionLayout(modifier = Modifier.fillMaxSize()) {
    LazyColumn(
        // ...
    ) {
        items(listSnacks) { snack ->
            AnimatedVisibility(
                visible = snack != selectedSnack,
                enter = fadeIn() + scaleIn(),
                exit = fadeOut() + scaleOut(),
                modifier = Modifier.animateItem()
            ) {
                Box(
                    modifier = Modifier
                        .sharedBounds(
                            sharedContentState = rememberSharedContentState(key = "${snack.name}-bounds"),
                            // Using the scope provided by AnimatedVisibility
                            animatedVisibilityScope = this,
                            clipInOverlayDuringTransition = OverlayClip(shapeForSharedElement)
                        )
                        .background(Color.White, shapeForSharedElement)
                        .clip(shapeForSharedElement)
                ) {
                    SnackContents(
                        snack = snack,
                        modifier = Modifier.sharedElement(
                            sharedContentState = rememberSharedContentState(key = snack.name),
                            animatedVisibilityScope = this@AnimatedVisibility
                        ),
                        onClick = {
                            selectedSnack = snack
                        }
                    )
                }
            }
        }
    }
    // Contains matching AnimatedContent with sharedBounds modifiers.
    SnackEditDetails(
        snack = selectedSnack,
        onConfirmClick = {
            selectedSnack = null
        }
    )
}

छठी इमेज. AnimatedVisibility के साथ शेयर किए गए एलिमेंट.

मॉडिफ़ायर का क्रम

Modifier.sharedElement() और Modifier.sharedBounds() के साथ-साथ Compose के बाकी कॉम्पोनेंट में भी, मॉडिफ़ायर के क्रम का ध्यान रखना ज़रूरी है. साइज़ पर असर डालने वाले मॉडिफ़ायर को गलत जगह पर रखने से, शेयर किए गए एलिमेंट मैचिंग के दौरान अचानक विज़ुअल जंप हो सकते हैं.

उदाहरण के लिए, अगर शेयर किए गए दो एलिमेंट पर पैडिंग मॉडिफ़ायर को अलग-अलग जगह पर रखा जाता है, तो ऐनिमेशन में विज़ुअल अंतर दिखता है.

var selectFirst by remember { mutableStateOf(true) }
val key = remember { Any() }
SharedTransitionLayout(
    Modifier
        .fillMaxSize()
        .padding(10.dp)
        .clickable {
            selectFirst = !selectFirst
        }
) {
    AnimatedContent(targetState = selectFirst, label = "AnimatedContent") { targetState ->
        if (targetState) {
            Box(
                Modifier
                    .padding(12.dp)
                    .sharedBounds(
                        rememberSharedContentState(key = key),
                        animatedVisibilityScope = this@AnimatedContent
                    )
                    .border(2.dp, Color.Red)
            ) {
                Text(
                    "Hello",
                    fontSize = 20.sp
                )
            }
        } else {
            Box(
                Modifier
                    .offset(180.dp, 180.dp)
                    .sharedBounds(
                        rememberSharedContentState(
                            key = key,
                        ),
                        animatedVisibilityScope = this@AnimatedContent
                    )
                    .border(2.dp, Color.Red)
                    // This padding is placed after sharedBounds, but it doesn't match the
                    // other shared elements modifier order, resulting in visual jumps
                    .padding(12.dp)

            ) {
                Text(
                    "Hello",
                    fontSize = 36.sp
                )
            }
        }
    }
}

मेल खाने वाली सीमाएं

मिलान न होने वाली सीमाएं: ध्यान दें कि शेयर किए गए एलिमेंट का ऐनिमेशन थोड़ा अलग दिखता है, क्योंकि इसे गलत सीमाओं के हिसाब से फिर से साइज़ करना पड़ता है

शेयर किए गए एलिमेंट के मॉडिफ़ायर से पहले इस्तेमाल किए गए मॉडिफ़ायर, शेयर किए गए एलिमेंट के मॉडिफ़ायर के लिए सीमाएं तय करते हैं. इनका इस्तेमाल, शुरुआती और टारगेट बाउंड्री तय करने के लिए किया जाता है. इसके बाद, बाउंड्री ऐनिमेशन के लिए इनका इस्तेमाल किया जाता है.

शेयर किए गए एलिमेंट के मॉडिफ़ायर के बाद इस्तेमाल किए गए मॉडिफ़ायर, चाइल्ड के टारगेट साइज़ को मेज़र और कैलकुलेट करने के लिए, पहले से मौजूद कंस्ट्रेंट का इस्तेमाल करते हैं. शेयर किए गए एलिमेंट मॉडिफ़ायर, ऐनिमेशन वाली कंस्ट्रेंट की एक सीरीज़ बनाते हैं. इससे चाइल्ड एलिमेंट, शुरुआती साइज़ से टारगेट साइज़ में धीरे-धीरे बदलता है.

हालांकि, अगर ऐनिमेशन के लिए resizeMode = ScaleToBounds() या कंपोज़ेबल पर Modifier.skipToLookaheadSize() का इस्तेमाल किया जाता है, तो ऐसा नहीं होता. इस मामले में, कंपोज़ टारगेट कंस्ट्रेंट का इस्तेमाल करके चाइल्ड को लेआउट करता है. साथ ही, लेआउट के साइज़ को बदलने के बजाय, ऐनिमेशन करने के लिए स्केल फ़ैक्टर का इस्तेमाल करता है.

यूनीक कुंजियां

शेयर किए गए जटिल एलिमेंट के साथ काम करते समय, ऐसी कुंजी बनाना सबसे सही तरीका है जो स्ट्रिंग न हो. ऐसा इसलिए, क्योंकि स्ट्रिंग के मैच होने में गड़बड़ी हो सकती है. मैच करने के लिए, हर कुंजी यूनीक होनी चाहिए. उदाहरण के लिए, Jetsnack में ये शेयर किए गए एलिमेंट मौजूद हैं:

सातवीं इमेज. Jetsnack की इमेज, जिसमें यूज़र इंटरफ़ेस (यूआई) के हर हिस्से के लिए एनोटेशन दिए गए हैं.

शेयर किए गए एलिमेंट के टाइप को दिखाने के लिए, एक enum बनाया जा सकता है. इस उदाहरण में, स्नैक कार्ड को होम स्क्रीन पर अलग-अलग जगहों पर भी दिखाया जा सकता है. जैसे, "लोकप्रिय" और "सुझाया गया" सेक्शन में. आपके पास ऐसी कुंजी बनाने का विकल्प होता है जिसमें शेयर किए जाने वाले एलिमेंट का snackId, origin ("लोकप्रिय" / "सुझाया गया"), और type शामिल हो:

data class SnackSharedElementKey(
    val snackId: Long,
    val origin: String,
    val type: SnackSharedElementType
)

enum class SnackSharedElementType {
    Bounds,
    Image,
    Title,
    Tagline,
    Background
}

@Composable
fun SharedElementUniqueKey() {
    // ...
            Box(
                modifier = Modifier
                    .sharedElement(
                        rememberSharedContentState(
                            key = SnackSharedElementKey(
                                snackId = 1,
                                origin = "latest",
                                type = SnackSharedElementType.Image
                            )
                        ),
                        animatedVisibilityScope = this@AnimatedVisibility
                    )
            )
            // ...
}

कुंजियों के लिए डेटा क्लास का सुझाव दिया जाता है, क्योंकि वे hashCode() और isEquals() लागू करती हैं.

शेयर किए गए एलिमेंट की विज़िबिलिटी को मैन्युअल तरीके से मैनेज करना

अगर आपको AnimatedVisibility या AnimatedContent का इस्तेमाल नहीं करना है, तो शेयर किए गए एलिमेंट की दृश्यता को खुद मैनेज किया जा सकता है. Modifier.sharedElementWithCallerManagedVisibility() का इस्तेमाल करें और अपनी शर्त दें. इससे यह तय होगा कि कोई आइटम कब दिखेगा और कब नहीं:

var selectFirst by remember { mutableStateOf(true) }
val key = remember { Any() }
SharedTransitionLayout(
    Modifier
        .fillMaxSize()
        .padding(10.dp)
        .clickable {
            selectFirst = !selectFirst
        }
) {
    Box(
        Modifier
            .sharedElementWithCallerManagedVisibility(
                rememberSharedContentState(key = key),
                !selectFirst
            )
            .background(Color.Red)
            .size(100.dp)
    ) {
        Text(if (!selectFirst) "false" else "true", color = Color.White)
    }
    Box(
        Modifier
            .offset(180.dp, 180.dp)
            .sharedElementWithCallerManagedVisibility(
                rememberSharedContentState(
                    key = key,
                ),
                selectFirst
            )
            .alpha(0.5f)
            .background(Color.Blue)
            .size(180.dp)
    ) {
        Text(if (selectFirst) "false" else "true", color = Color.White)
    }
}

मौजूदा सीमाएं

इन एपीआई की कुछ सीमाएं हैं. खास तौर पर:

  • Views और Compose के बीच इंटरऑपरेबिलिटी काम नहीं करती. इसमें AndroidView को रैप करने वाला कोई भी कंपोज़ेबल शामिल है. जैसे, Dialog या ModalBottomSheet.
  • इनके लिए, अपने-आप ऐनिमेशन बनाने की सुविधा उपलब्ध नहीं है:
    • शेयर किए गए इमेज कंपोज़ेबल:
      • ContentScale में डिफ़ॉल्ट रूप से ऐनिमेशन नहीं होता. यह सेट किए गए आखिरी समय ContentScale पर स्नैप हो जाता है.
    • शेप क्लिपिंग - शेप के बीच अपने-आप ऐनिमेशन होने की सुविधा उपलब्ध नहीं है. उदाहरण के लिए, आइटम के ट्रांज़िशन के दौरान स्क्वेयर से सर्कल में ऐनिमेशन होना.
    • जिन मामलों में sharedElement() का इस्तेमाल नहीं किया जा सकता उनके लिए, sharedElement() के बजाय Modifier.sharedBounds() का इस्तेमाल करें. साथ ही, आइटम में Modifier.animateEnterExit() जोड़ें.