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

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

রচনা CompositionLocal ভূমিকা

সাধারণত Compose-এ, প্রতিটি কম্পোজেবল ফাংশনের প্যারামিটার হিসেবে ডেটা 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 হল Material থিম যা ব্যবহার করে। MaterialTheme হল এমন একটি অবজেক্ট যা তিনটি CompositionLocal ইনস্ট্যান্স প্রদান করে: colorScheme , typography এবং shapes , যা আপনাকে Composition এর যেকোনো ডিসেন্ডেন্ট অংশে পরে এগুলি পুনরুদ্ধার করতে দেয়। বিশেষ করে, এগুলি হল 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 ইনস্ট্যান্সকে Composition-এর একটি অংশে সীমাবদ্ধ করা হয় যাতে আপনি গাছের বিভিন্ন স্তরে বিভিন্ন মান প্রদান করতে পারেন। একটি CompositionLocal এর current মান Composition-এর সেই অংশে পূর্বপুরুষ দ্বারা প্রদত্ত নিকটতম মানের সাথে মিলে যায়।

একটি CompositionLocal কে একটি নতুন মান প্রদান করতে, CompositionLocalProvider এবং এর provides infix ফাংশন ব্যবহার করুন যা একটি CompositionLocal কীকে একটি value সাথে সংযুক্ত করে। CompositionLocalProvider এর content ল্যাম্বডা CompositionLocal এর current সম্পত্তি অ্যাক্সেস করার সময় প্রদত্ত মান পাবে। যখন একটি নতুন মান প্রদান করা হয়, তখন Compose Composition এর এমন অংশগুলিকে পুনরায় কম্পোজ করে যা 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")
}

CompositionLocalExample কম্পোজেবলের প্রিভিউ।
চিত্র ১. CompositionLocalExample কম্পোজেবলের প্রিভিউ।

শেষ উদাহরণে, CompositionLocal ইনস্ট্যান্সগুলি অভ্যন্তরীণভাবে Material composables দ্বারা ব্যবহৃত হয়েছিল। 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 হল একটি টুল যা Composition-এর মাধ্যমে পরোক্ষভাবে ডেটা প্রেরণ করে।

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

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

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

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

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

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

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

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

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

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

একটি CompositionLocal তৈরি করুন স্থানীয়

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

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

  • staticCompositionLocalOf : compositionLocalOf বিপরীতে, staticCompositionLocalOf এর পঠনগুলি Compose দ্বারা ট্র্যাক করা হয় না। মান পরিবর্তন করলে Lambda-তে CompositionLocal প্রদান করা সম্পূর্ণ content পুনরায় কম্পোজ করা হয়, শুধুমাত্র Composition-এ 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 infix ফাংশনটি ব্যবহার করুন যা একটি CompositionLocal কীকে একটি value সাথে সংযুক্ত করে নিম্নরূপ:

// 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()
    }
}

{% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %} {% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %}