CompositionLocal সহ স্থানীয়ভাবে স্কোপড ডেটা

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

CompositionLocal প্রবর্তন করা হচ্ছে

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

@Composable
fun MyApp() {
    // Theme information tends to be defined near the root of the application
    val colors = colors()
}

// Some composable deep in the hierarchy
@Composable
fun SomeTextLabel(labelText: String) {
    Text(
        text = labelText,
        color = colors.onPrimary // ← need to access colors here
    )
}

বেশিরভাগ কম্পোজেবলগুলিতে একটি স্পষ্ট প্যারামিটার নির্ভরতা হিসাবে রঙগুলি পাস করার প্রয়োজন না হওয়াকে সমর্থন করার জন্য, কম্পোজ CompositionLocal অফার করে যা আপনাকে ট্রি-স্কোপযুক্ত নামযুক্ত বস্তু তৈরি করতে দেয় যা UI ট্রির মাধ্যমে ডেটা প্রবাহের অন্তর্নিহিত উপায় হিসাবে ব্যবহার করা যেতে পারে।

CompositionLocal উপাদানগুলি সাধারণত UI গাছের একটি নির্দিষ্ট নোডে একটি মান সহ সরবরাহ করা হয়। কম্পোজেবল ফাংশনে CompositionLocal প্যারামিটার হিসেবে ঘোষণা না করেই সেই মানটি তার কম্পোজযোগ্য বংশধরদের দ্বারা ব্যবহার করা যেতে পারে।

CompositionLocal হল যা ম্যাটেরিয়াল থিম হুডের নিচে ব্যবহার করে। MaterialTheme হল একটি বস্তু যা তিনটি CompositionLocal স্থানীয় দৃষ্টান্ত প্রদান করে: colorScheme , typography এবং shapes , যা আপনাকে কম্পোজিশনের যেকোনো বংশধর অংশে পরে সেগুলি পুনরুদ্ধার করতে দেয়৷ বিশেষত, এগুলি হল LocalColorScheme , LocalShapes , এবং LocalTypography বৈশিষ্ট্য যা আপনি MaterialTheme colorScheme , shapes এবং typography বৈশিষ্ট্যগুলির মাধ্যমে অ্যাক্সেস করতে পারেন৷

@Composable
fun MyApp() {
    // Provides a Theme whose values are propagated down its `content`
    MaterialTheme {
        // New values for colorScheme, typography, and shapes are available
        // in MaterialTheme's content lambda.

        // ... content here ...
    }
}

// Some composable deep in the hierarchy of MaterialTheme
@Composable
fun SomeTextLabel(labelText: String) {
    Text(
        text = labelText,
        // `primary` is obtained from MaterialTheme's
        // LocalColors CompositionLocal
        color = MaterialTheme.colorScheme.primary
    )
}

একটি CompositionLocal উদাহরণ কম্পোজিশনের একটি অংশে স্কোপ করা হয় যাতে আপনি গাছের বিভিন্ন স্তরে বিভিন্ন মান প্রদান করতে পারেন। একটি CompositionLocal এর current মান কম্পোজিশনের সেই অংশে পূর্বপুরুষের দ্বারা প্রদত্ত নিকটতম মানের সাথে মিলে যায়।

একটি CompositionLocal এ একটি নতুন মান প্রদান করতে, CompositionLocalProvider ব্যবহার করুন এবং এটি ইনফিক্স ফাংশন provides যা একটি value সাথে একটি CompositionLocal কী যুক্ত করে। CompositionLocalProvider এর content ল্যাম্বডা CompositionLocal এর current সম্পত্তি অ্যাক্সেস করার সময় প্রদত্ত মান পাবে। যখন একটি নতুন মান প্রদান করা হয়, তখন কম্পোজ কম্পোজিশনের অংশগুলিকে পুনরায় কম্পোজ করে যা CompositionLocal পড়ে।

এর উদাহরণ হিসেবে, LocalContentColor CompositionLocal টেক্সট এবং আইকনোগ্রাফির জন্য ব্যবহৃত পছন্দের বিষয়বস্তুর রঙ রয়েছে যাতে এটি বর্তমান পটভূমির রঙের সাথে বৈপরীত্য নিশ্চিত করে। নিম্নলিখিত উদাহরণে, CompositionLocalProvider কম্পোজিশনের বিভিন্ন অংশের জন্য বিভিন্ন মান প্রদান করতে ব্যবহৃত হয়।

@Composable
fun CompositionLocalExample() {
    MaterialTheme {
        // Surface provides contentColorFor(MaterialTheme.colorScheme.surface) by default
        // This is to automatically make text and other content contrast to the background
        // correctly.
        Surface {
            Column {
                Text("Uses Surface's provided content color")
                CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.primary) {
                    Text("Primary color provided by LocalContentColor")
                    Text("This Text also uses primary as textColor")
                    CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.error) {
                        DescendantExample()
                    }
                }
            }
        }
    }
}

@Composable
fun DescendantExample() {
    // CompositionLocalProviders also work across composable functions
    Text("This Text uses the error color now")
}

চিত্র 1. CompositionLocalExample প্রিভিউ LocalExample composable.

শেষ উদাহরণে, CompositionLocal দৃষ্টান্তগুলি অভ্যন্তরীণভাবে উপাদান কম্পোজেবল দ্বারা ব্যবহৃত হয়েছিল। একটি CompositionLocal এর বর্তমান মান অ্যাক্সেস করতে, এর current সম্পত্তি ব্যবহার করুন। নিম্নলিখিত উদাহরণে, LocalContext CompositionLocal এর বর্তমান Context মান যা সাধারণত Android অ্যাপগুলিতে ব্যবহৃত হয় পাঠ্য বিন্যাস করতে ব্যবহৃত হয়:

@Composable
fun FruitText(fruitSize: Int) {
    // Get `resources` from the current value of LocalContext
    val resources = LocalContext.current.resources
    val fruitText = remember(resources, fruitSize) {
        resources.getQuantityString(R.plurals.fruit_title, fruitSize)
    }
    Text(text = fruitText)
}

আপনার নিজস্ব CompositionLocal তৈরি করা

CompositionLocal হল একটি টুল যা কম্পোজিশনের মাধ্যমে ডাটা ডাউন পাস করার জন্য

CompositionLocal ব্যবহার করার জন্য আরেকটি মূল সংকেত হল যখন প্যারামিটারটি ক্রস-কাটিং করা হয় এবং বাস্তবায়নের মধ্যবর্তী স্তরগুলি এটির অস্তিত্ব সম্পর্কে সচেতন হওয়া উচিত নয় , কারণ এই মধ্যবর্তী স্তরগুলিকে সচেতন করা কম্পোজেবলের উপযোগিতাকে সীমিত করবে। উদাহরণস্বরূপ, অ্যান্ড্রয়েড অনুমতিগুলির জন্য অনুসন্ধানের জন্য হুডের নীচে একটি CompositionLocal দ্বারা সরবরাহ করা হয়৷ একটি মিডিয়া পিকার কম্পোজেবল তার API পরিবর্তন না করে ডিভাইসে অনুমতি-সুরক্ষিত সামগ্রী অ্যাক্সেস করার জন্য নতুন কার্যকারিতা যোগ করতে পারে এবং মিডিয়া পিকারের কলকারীদের পরিবেশ থেকে ব্যবহৃত এই অতিরিক্ত প্রসঙ্গ সম্পর্কে সচেতন হতে হবে।

যাইহোক, CompositionLocal সর্বদা সর্বোত্তম সমাধান নয়। আমরা CompositionLocal অতিরিক্ত ব্যবহারকে নিরুৎসাহিত করি কারণ এটি কিছু খারাপ দিক নিয়ে আসে:

CompositionLocal একটি কম্পোজেবল এর আচরণ সম্পর্কে যুক্তি করা কঠিন করে তোলে । যেহেতু তারা অন্তর্নিহিত নির্ভরতা তৈরি করে, কম্পোজেবলের কলকারীরা যেগুলি তাদের ব্যবহার করে তাদের নিশ্চিত করতে হবে যে প্রতিটি CompositionLocal জন্য একটি মান সন্তুষ্ট।

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

CompositionLocal ব্যবহার করবেন কিনা তা স্থির করা

কিছু শর্ত রয়েছে যা CompositionLocal আপনার ব্যবহারের ক্ষেত্রে একটি ভাল সমাধান করতে পারে:

একটি CompositionLocal একটি ভাল ডিফল্ট মান থাকা উচিত । যদি কোনও ডিফল্ট মান না থাকে, তাহলে আপনাকে অবশ্যই গ্যারান্টি দিতে হবে যে একজন ডেভেলপারের পক্ষে এমন পরিস্থিতিতে যাওয়া অত্যন্ত কঠিন যেখানে CompositionLocal জন্য একটি মান প্রদান করা হয় না। একটি ডিফল্ট মান প্রদান না করা সমস্যা এবং হতাশার কারণ হতে পারে পরীক্ষা তৈরি করার সময় বা একটি কম্পোজেবল প্রিভিউ করার সময় যেটি CompositionLocal ব্যবহার করে তা স্পষ্টভাবে প্রদান করতে হবে।

ট্রি-স্কোপড বা সাব-হায়ারার্কি স্কোপড বলে মনে করা হয় না এমন ধারণাগুলির জন্য CompositionLocal এড়িয়ে চলুন । একটি CompositionLocal অর্থবোধক হয় যখন এটি সম্ভাব্যভাবে যেকোনো বংশধর দ্বারা ব্যবহার করা যেতে পারে, তাদের মধ্যে কয়েকটি দ্বারা নয়।

যদি আপনার ব্যবহারের ক্ষেত্রে এই প্রয়োজনীয়তাগুলি পূরণ না করে, একটি CompositionLocal তৈরি করার আগে বিবেচনা করার জন্য বিকল্প বিভাগটি দেখুন।

একটি খারাপ অনুশীলনের একটি উদাহরণ হল একটি CompositionLocal তৈরি করা যা একটি নির্দিষ্ট স্ক্রিনের ViewModel ধারণ করে যাতে সেই স্ক্রিনের সমস্ত কম্পোজেবল কিছু যুক্তি সম্পাদন করার জন্য ViewModel একটি রেফারেন্স পেতে পারে। এটি একটি খারাপ অভ্যাস কারণ একটি নির্দিষ্ট UI ট্রির নীচে সমস্ত কম্পোজেবলের একটি ViewModel সম্পর্কে জানার প্রয়োজন নেই৷ ভাল অভ্যাস হল কম্পোজেবলের কাছে শুধুমাত্র সেই তথ্যগুলি প্রেরণ করা যা তাদের প্রয়োজনীয় প্যাটার্ন অনুসরণ করে যে অবস্থাটি নিচে প্রবাহিত হয় এবং ঘটনাগুলি উপরে উঠে যায় । এই পদ্ধতিটি আপনার কম্পোজেবলগুলিকে আরও পুনঃব্যবহারযোগ্য এবং পরীক্ষা করা সহজ করে তুলবে।

একটি CompositionLocal তৈরি করা

একটি CompositionLocal তৈরি করার জন্য দুটি API আছে:

  • compositionLocalOf : পুনর্গঠনের সময় প্রদত্ত মান পরিবর্তন করা শুধুমাত্র সেই বিষয়বস্তুটিকে বাতিল করে যা এর current মান পড়ে।

  • staticCompositionLocalOf : compositionLocalOf বিপরীতে, একটি staticCompositionLocalOf এর রিডগুলি রচনা দ্বারা ট্র্যাক করা হয় না। মান পরিবর্তন করা হলে সমগ্র content ল্যাম্বডা যেখানে CompositionLocal পুনরায় কম্পোজ করার জন্য প্রদান করা হয়, শুধুমাত্র সেই জায়গাগুলির পরিবর্তে যেখানে current মানটি রচনায় পড়া হয়।

CompositionLocal এ প্রদত্ত মান যদি পরিবর্তন হওয়ার সম্ভাবনা খুব কম হয় বা কখনই পরিবর্তন হবে না, কর্মক্ষমতা সুবিধা পেতে staticCompositionLocalOf ব্যবহার করুন।

উদাহরণস্বরূপ, UI উপাদানের জন্য একটি ছায়া ব্যবহার করে কম্পোজেবলগুলিকে যেভাবে উন্নত করা হয় সেভাবে একটি অ্যাপের ডিজাইন সিস্টেম মতামত দেওয়া যেতে পারে। যেহেতু অ্যাপের বিভিন্ন উচ্চতা সমগ্র UI ট্রি জুড়ে প্রচারিত হওয়া উচিত, তাই আমরা একটি CompositionLocal ব্যবহার করি। যেহেতু CompositionLocal মান শর্তসাপেক্ষে সিস্টেম থিমের উপর ভিত্তি করে প্রাপ্ত হয়, আমরা compositionLocalOf API ব্যবহার করি:

// LocalElevations.kt file

data class Elevations(val card: Dp = 0.dp, val default: Dp = 0.dp)

// Define a CompositionLocal global object with a default
// This instance can be accessed by all composables in the app
val LocalElevations = compositionLocalOf { Elevations() }

একটি CompositionLocal মান প্রদান

CompositionLocalProvider composable মানগুলিকে CompositionLocal দৃষ্টান্তের সাথে প্রদত্ত অনুক্রমের জন্য আবদ্ধ করে । একটি CompositionLocal এ একটি নতুন মান প্রদান করতে, provides ইনফিক্স ফাংশন ব্যবহার করুন যা নিম্নরূপ একটি value সাথে একটি CompositionLocal কী যুক্ত করে:

// MyActivity.kt file

class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // Calculate elevations based on the system theme
            val elevations = if (isSystemInDarkTheme()) {
                Elevations(card = 1.dp, default = 1.dp)
            } else {
                Elevations(card = 0.dp, default = 0.dp)
            }

            // Bind elevation as the value for LocalElevations
            CompositionLocalProvider(LocalElevations provides elevations) {
                // ... Content goes here ...
                // This part of Composition will see the `elevations` instance
                // when accessing LocalElevations.current
            }
        }
    }
}

CompositionLocal গ্রাস করা

CompositionLocal.current নিকটতম CompositionLocalProvider দ্বারা প্রদত্ত মান প্রদান করে যা সেই CompositionLocal কে একটি মান প্রদান করে:

@Composable
fun SomeComposable() {
    // Access the globally defined LocalElevations variable to get the
    // current Elevations in this part of the Composition
    MyCard(elevation = LocalElevations.current.card) {
        // Content
    }
}

বিবেচনা করার বিকল্প

একটি CompositionLocal কিছু ব্যবহারের ক্ষেত্রে একটি অত্যধিক সমাধান হতে পারে। যদি আপনার ব্যবহারের ক্ষেত্রে CompositionLocal বিভাগ ব্যবহার করার সিদ্ধান্ত নেওয়ার মধ্যে নির্দিষ্ট মানদণ্ড পূরণ না করে, তাহলে অন্য সমাধান সম্ভবত আপনার ব্যবহারের ক্ষেত্রে আরও উপযুক্ত হতে পারে।

সুস্পষ্ট পরামিতি পাস

কম্পোজেবলের নির্ভরতা সম্পর্কে স্পষ্ট হওয়া একটি ভাল অভ্যাস। আমরা সুপারিশ করি যে আপনি কম্পোজেবল পাস করুন শুধুমাত্র তাদের যা প্রয়োজন । কম্পোজেবলের ডিকপলিং এবং পুনঃব্যবহারকে উত্সাহিত করার জন্য, প্রতিটি কম্পোজেবলের সম্ভাব্য সর্বনিম্ন পরিমাণ তথ্য রাখা উচিত।

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    MyDescendant(myViewModel.data)
}

// Don't pass the whole object! Just what the descendant needs.
// Also, don't  pass the ViewModel as an implicit dependency using
// a CompositionLocal.
@Composable
fun MyDescendant(myViewModel: MyViewModel) { /* ... */ }

// Pass only what the descendant needs
@Composable
fun MyDescendant(data: DataToDisplay) {
    // Display data
}

নিয়ন্ত্রণের বিপরীত

একটি কম্পোজেবলে অপ্রয়োজনীয় নির্ভরতা পাস করা এড়ানোর আরেকটি উপায় হল নিয়ন্ত্রণের বিপরীতের মাধ্যমে। বংশধর কিছু যুক্তি কার্যকর করার জন্য নির্ভরতা গ্রহণ করার পরিবর্তে, পিতামাতা এটি করেন।

নিম্নলিখিত উদাহরণটি দেখুন যেখানে একজন বংশধরকে কিছু ডেটা লোড করার অনুরোধটি ট্রিগার করতে হবে:

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    MyDescendant(myViewModel)
}

@Composable
fun MyDescendant(myViewModel: MyViewModel) {
    Button(onClick = { myViewModel.loadData() }) {
        Text("Load data")
    }
}

মামলার উপর নির্ভর করে, MyDescendant অনেক দায়িত্ব থাকতে পারে। এছাড়াও, MyViewModel নির্ভরতা হিসাবে পাস করা MyDescendant কম পুনঃব্যবহারযোগ্য করে তোলে কারণ তারা এখন একত্রিত হয়েছে। বিকল্পটি বিবেচনা করুন যা বংশধরের মধ্যে নির্ভরতা পাস করে না এবং নিয়ন্ত্রণ নীতিগুলির বিপরীত ব্যবহার করে যা পূর্বপুরুষকে যুক্তি সম্পাদনের জন্য দায়ী করে:

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    ReusableLoadDataButton(
        onLoadClick = {
            myViewModel.loadData()
        }
    )
}

@Composable
fun ReusableLoadDataButton(onLoadClick: () -> Unit) {
    Button(onClick = onLoadClick) {
        Text("Load data")
    }
}

এই পদ্ধতিটি কিছু ব্যবহারের ক্ষেত্রে আরও উপযুক্ত হতে পারে কারণ এটি শিশুকে তার নিকটবর্তী পূর্বপুরুষদের থেকে আলাদা করে দেয় । পূর্বপুরুষ কম্পোজেবলগুলি আরও নমনীয় নিম্ন-স্তরের কম্পোজেবল থাকার পক্ষে আরও জটিল হয়ে ওঠে।

একইভাবে, @Composable কন্টেন্ট ল্যাম্বডাস একই সুবিধা পেতে একই ভাবে ব্যবহার করা যেতে পারে :

@Composable
fun MyComposable(myViewModel: MyViewModel = viewModel()) {
    // ...
    ReusablePartOfTheScreen(
        content = {
            Button(
                onClick = {
                    myViewModel.loadData()
                }
            ) {
                Text("Confirm")
            }
        }
    )
}

@Composable
fun ReusablePartOfTheScreen(content: @Composable () -> Unit) {
    Column {
        // ...
        content()
    }
}

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}