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() का इस्तेमाल किया जाता है, तो ऐसा नहीं होता. इस मामले में, Compose टारगेट की पाबंदियों का इस्तेमाल करके चाइल्ड लेआउट को सेट करता है. साथ ही, लेआउट के साइज़ में बदलाव करने के बजाय, ऐनिमेशन को दिखाने के लिए स्केल फ़ैक्टर का इस्तेमाल करता है.

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

जटिल शेयर किए गए एलिमेंट के साथ काम करते समय, ऐसी कुंजी बनाना एक अच्छा तरीका है जो स्ट्रिंग न हो. ऐसा इसलिए, क्योंकि स्ट्रिंग मैच करने में गड़बड़ी हो सकती है. मैच होने के लिए, हर कुंजी यूनीक होनी चाहिए. उदाहरण के लिए, 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() और equals() को लागू करती हैं.

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

अगर 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)
    }
}

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

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

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