রচনায় ভাগ করা উপাদান রূপান্তর

শেয়ার্ড এলিমেন্ট ট্রানজিশন হলো এমন একটি নির্বিঘ্ন উপায়, যার মাধ্যমে একই ধরনের কন্টেন্টযুক্ত কম্পোজেবল এলিমেন্টগুলোর মধ্যে নির্বিঘ্নে পরিবর্তন করা যায়। এগুলো প্রায়শই নেভিগেশনের জন্য ব্যবহৃত হয়, যা ব্যবহারকারীকে বিভিন্ন স্ক্রিনের মধ্যে নেভিগেট করার সময় সেগুলোকে দৃশ্যত সংযুক্ত করার সুযোগ দেয়।

উদাহরণস্বরূপ, নিচের ভিডিওটিতে আপনি দেখতে পাবেন যে, স্ন্যাকটির ছবি ও শিরোনাম লিস্টিং পেজ থেকে ডিটেইল পেজে শেয়ার করা হচ্ছে।

চিত্র ১. জেটস্নাক শেয়ার্ড এলিমেন্টের ডেমো।

Compose-এ কয়েকটি উচ্চ-স্তরের API আছে যা আপনাকে শেয়ার্ড এলিমেন্ট তৈরি করতে সাহায্য করে:

  • SharedTransitionLayout : শেয়ার্ড এলিমেন্ট ট্রানজিশন বাস্তবায়নের জন্য প্রয়োজনীয় সর্ববহিঃস্থ লেআউট। এটি একটি SharedTransitionScope প্রদান করে। শেয়ার্ড এলিমেন্ট মডিফায়ার ব্যবহার করার জন্য কম্পোজেবলগুলোকে অবশ্যই একটি SharedTransitionScope মধ্যে থাকতে হবে।
  • Modifier.sharedElement() : এই মডিফায়ারটি SharedTransitionScope কে সেই কম্পোজেবলটিকে চিহ্নিত করে, যেটিকে অন্য একটি কম্পোজেবলের সাথে মেলানো উচিত।
  • Modifier.sharedBounds() : এই মডিফায়ারটি SharedTransitionScope কে জানিয়ে দেয় যে, ট্রানজিশনটি যেখানে ঘটবে, সেই কন্টেইনারের সীমানা হিসেবে এই কম্পোজেবলটির সীমানা ব্যবহার করা উচিত। sharedElement() এর বিপরীতে, sharedBounds() দৃশ্যত ভিন্ন ধরনের কন্টেন্টের জন্য ডিজাইন করা হয়েছে।

কম্পোজে শেয়ার্ড এলিমেন্ট তৈরি করার সময় একটি গুরুত্বপূর্ণ ধারণা হলো ওভারলে এবং ক্লিপিং কীভাবে কাজ করে। এই গুরুত্বপূর্ণ বিষয়টি সম্পর্কে আরও জানতে ক্লিপিং এবং ওভারলে বিভাগটি দেখুন।

মৌলিক ব্যবহার

এই বিভাগে নিম্নলিখিত রূপান্তরটি তৈরি করা হবে, যা ছোট "তালিকা" আইটেম থেকে বড় ও বিস্তারিত আইটেমে স্থানান্তরিত করবে:

চিত্র ২. দুটি কম্পোজেবলের মধ্যে একটি শেয়ার্ড এলিমেন্ট ট্রানজিশনের মৌলিক উদাহরণ।

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 এবং ColumnModifier.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 প্রদান করে। নিশ্চিত করুন যে এটি আপনার UI হায়ারার্কির সেই একই শীর্ষ-স্তরের স্থানে রাখা হয়েছে যেখানে আপনি শেয়ার করতে চান এমন এলিমেন্টগুলো রয়েছে।

সাধারণত, কম্পোজেবলগুলোকেও একটি 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 দিয়ে মোড়ানো হয়েছে। যখন আইটেমটিতে ক্লিক করা হয়, তখন কন্টেন্টটি UI থেকে বেরিয়ে এসে একটি ডায়ালগ-সদৃশ কম্পোনেন্টে চলে আসার মতো একটি ভিজ্যুয়াল ইফেক্ট তৈরি করে।

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 টার্গেট কনস্ট্রেইন্ট ব্যবহার করে চাইল্ডটিকে লেআউট করে এবং লেআউটের আকার পরিবর্তন করার পরিবর্তে অ্যানিমেশনটি সম্পাদনের জন্য একটি স্কেল ফ্যাক্টর ব্যবহার করে।

অনন্য চাবি

জটিল শেয়ার্ড এলিমেন্ট নিয়ে কাজ করার সময়, এমন একটি কী (key) তৈরি করা ভালো যা স্ট্রিং নয়, কারণ স্ট্রিং ম্যাচ করার ক্ষেত্রে ভুল হওয়ার সম্ভাবনা থাকে। ম্যাচ হওয়ার জন্য প্রতিটি কী অবশ্যই অনন্য হতে হবে। উদাহরণস্বরূপ, Jetsnack-এ আমাদের নিম্নলিখিত শেয়ার্ড এলিমেন্টগুলো রয়েছে:

চিত্র ৭। জেটস্নাকের ছবি, যেখানে ইউজার ইন্টারফেসের প্রতিটি অংশের জন্য টীকা দেওয়া আছে।

আপনি শেয়ার করা এলিমেন্টের ধরন বোঝানোর জন্য একটি enum তৈরি করতে পারেন। এই উদাহরণে, পুরো স্নাক কার্ডটি হোম স্ক্রিনের একাধিক ভিন্ন জায়গা থেকেও প্রদর্শিত হতে পারে, যেমন একটি "জনপ্রিয়" এবং একটি "প্রস্তাবিত" বিভাগে। আপনি এমন একটি কী (key) তৈরি করতে পারেন যাতে থাকবে 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
                    )
            )
            // ...
}

কী (key) হিসেবে ডেটা ক্লাস ব্যবহার করার পরামর্শ দেওয়া হয়, কারণ এগুলো 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)
    }
}

বর্তমান সীমাবদ্ধতা

এই এপিআইগুলোর কিছু সীমাবদ্ধতা রয়েছে। সবচেয়ে উল্লেখযোগ্য হলো:

  • ভিউ এবং কম্পোজের মধ্যে কোনো আন্তঃকার্যক্ষমতা সমর্থিত নয়। এর মধ্যে AndroidView র‍্যাপ করে এমন যেকোনো কম্পোজেবল অন্তর্ভুক্ত, যেমন Dialog বা ModalBottomSheet
  • নিম্নলিখিতগুলির জন্য কোনো স্বয়ংক্রিয় অ্যানিমেশন সমর্থন নেই:
    • শেয়ার করা ইমেজ কম্পোজেবলস :
      • ডিফল্টরূপে ContentScale অ্যানিমেটেড হয় না। এটি সেট করা শেষ ContentScale এ স্ন্যাপ করে।
    • আকৃতি ক্লিপিং - আকৃতিগুলোর মধ্যে স্বয়ংক্রিয় অ্যানিমেশনের জন্য কোনো অন্তর্নির্মিত সমর্থন নেই - উদাহরণস্বরূপ, আইটেমটি স্থানান্তরিত হওয়ার সাথে সাথে একটি বর্গক্ষেত্র থেকে বৃত্তে অ্যানিমেট করা।
    • যেসব ক্ষেত্রে এটি সমর্থিত নয়, সেখানে sharedElement() এর পরিবর্তে Modifier.sharedBounds() ব্যবহার করুন এবং আইটেমগুলোতে Modifier.animateEnterExit() যোগ করুন।