ComposeLocal के साथ स्थानीय तौर पर स्कोप वाला डेटा

CompositionLocal कंपोज़िशन के ज़रिए डेटा ट्रांसफ़र कर सकता है. इस पेज पर, आपको CompositionLocal के बारे में ज़्यादा जानें और इसे बनाने का तरीका जानें CompositionLocal और जानें कि क्या CompositionLocal एक अच्छा समाधान है आपके इस्तेमाल का उदाहरण.

पेश है CompositionLocal

आम तौर पर, Compose में, डेटा नीचे की ओर जाता है हर कंपोज़ेबल फ़ंक्शन के पैरामीटर के तौर पर यूआई ट्री. इससे एक कंपोज़ेबल डिपेंडेंसी साफ़ तौर पर दी गई है. हालांकि, यह ऐसे डेटा के लिए बोझिल हो सकता है जो अक्सर और बड़े पैमाने पर इस्तेमाल किए जाते हैं, जैसे कि रंग या टाइप स्टाइल. नीचे दी गई जानकारी देखें उदाहरण:

@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
    )
}

इसके लिए, रंगों को साफ़ तौर पर पैरामीटर डिपेंडेंसी के तौर पर पास करने की ज़रूरत न हो Compose CompositionLocal पर मिलने वाले ऑफ़र से, आपको ज़्यादातर कंपोज़ेबल में का इस्तेमाल करके ट्री के स्कोप वाले नाम वाले ऑब्जेक्ट बनाए जा सकते हैं. इनका इस्तेमाल करके, यूज़र इंटरफ़ेस ट्री के ज़रिए डेटा फ़्लो का इस्तेमाल करें.

आम तौर पर, CompositionLocal एलिमेंट किसी खास नोड में वैल्यू के साथ दिए जाते हैं पर क्लिक करें. उस वैल्यू का इस्तेमाल, उसके कंपोज़ेबल डिसेंडेंट के बिना किया जा सकता है कंपोज़ेबल फ़ंक्शन में पैरामीटर के तौर पर CompositionLocal का एलान करता है.

मटीरियल थीम में CompositionLocal का इस्तेमाल किया जाता है. MaterialTheme है एक ऑब्जेक्ट जो तीन CompositionLocal इंस्टेंस——रंग, टाइपोग्राफ़ी, देता है और आकार—जिससे आप उन्हें बाद में किसी भी वंशज के रूप में कंपोज़िशन. खास तौर पर, ये हैं LocalColors, LocalShapes, और LocalTypography प्रॉपर्टी, जिन्हें MaterialTheme पर जाकर ऐक्सेस किया जा सकता है colors, shapes, और typography एट्रिब्यूट.

@Composable
fun MyApp() {
    // Provides a Theme whose values are propagated down its `content`
    MaterialTheme {
        // New values for colors, 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.colors.primary
    )
}

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

CompositionLocal को नई वैल्यू देने के लिए, CompositionLocalProvider और इसके provides इनफ़िक्स फ़ंक्शन का इस्तेमाल करें जो CompositionLocal कुंजी को value से जोड़ता है. कॉन्टेंट बनाने CompositionLocalProvider में से content लैम्डा को दिया जाएगा वैल्यू, CompositionLocal की current प्रॉपर्टी को ऐक्सेस करते समय लागू होती है. जब कोई नई वैल्यू डालने पर, कंपोज़िशन में उस कंपोज़िशन के हिस्से को फिर से शामिल किया जाता है जिस पर क्लिक किया जाता है CompositionLocal.

उदाहरण के लिए, LocalContentAlpha CompositionLocal में पसंदीदा कॉन्टेंट ऐल्फ़ा शामिल है, जिसका इस्तेमाल टेक्स्ट और आइकॉनोग्राफ़ी का इस्तेमाल करें. इस नीचे दिए गए उदाहरण में, CompositionLocalProvider का इस्तेमाल अलग-अलग कंपोज़िशन के अलग-अलग हिस्सों की वैल्यू शामिल हैं.

@Composable
fun CompositionLocalExample() {
    MaterialTheme { // MaterialTheme sets ContentAlpha.high as default
        Column {
            Text("Uses MaterialTheme's provided alpha")
            CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                Text("Medium value provided for LocalContentAlpha")
                Text("This Text also uses the medium value")
                CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
                    DescendantExample()
                }
            }
        }
    }
}

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

पहला डायग्राम. CompositionLocalExample कंपोज़ेबल की झलक.

ऊपर दिए गए सभी उदाहरणों में, CompositionLocal इंस्टेंस का अंदरूनी इस्तेमाल किया गया है मटीरियल कंपोज़ेबल से. CompositionLocal की मौजूदा वैल्यू को ऐक्सेस करने के लिए, इसके current का इस्तेमाल करें प्रॉपर्टी. यहां दिए गए उदाहरण में, LocalContext की मौजूदा Context वैल्यू CompositionLocal को फ़ॉर्मैट करने के लिए आम तौर पर 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 का इस्तेमाल करने का दूसरा कुंजी सिग्नल यह है कि पैरामीटर लागू करने की क्रॉस-कटिंग और इंटरमीडिएट लेयर्स को पता नहीं होना चाहिए यह मौजूद है, क्योंकि उन इंटरमीडिएट लेयर को जानकारी देने से, कंपोज़ेबल का इस्तेमाल कर रही हूँ. उदाहरण के लिए, Android की अनुमतियों के लिए क्वेरी करना CompositionLocal की ओर से उपलब्ध कराया जाता है. ऐसा मीडिया पिकर जिसे कंपोज़ किया जा सकता है पर अनुमति से सुरक्षित कॉन्टेंट को ऐक्सेस करने के लिए नई सुविधा जोड़ सकता है: डिवाइस, अपना एपीआई बदले बिना और मीडिया पिकर के कॉलर को पर्यावरण से जुड़े इस अतिरिक्त संदर्भ के बारे में जानें.

हालांकि, CompositionLocal हमेशा सबसे अच्छा समाधान नहीं होता है. बुध CompositionLocal के बहुत ज़्यादा इस्तेमाल करने से बचें, क्योंकि इसके कुछ नकारात्मक पहलू हैं:

CompositionLocal की वजह से किसी कंपोज़ेबल के काम करने के तरीके के बारे में सोच-विचार करना मुश्किल हो जाता है. जैसे ये इंप्लिसिट डिपेंडेंसी, कंपोज़ेबल के कॉलर बनाते हैं, जिनकी ज़रूरत होती है ताकि यह पक्का किया जा सके कि हर CompositionLocal के लिए वैल्यू पूरी हो रही है.

इसके अलावा, यह भी हो सकता है कि इस डिपेंडेंसी के बारे में साफ़ तौर पर कोई जानकारी मौजूद न हो, क्योंकि कंपोज़िशन के किसी भी हिस्से में बदलाव किया जा सकता है. इसलिए, ऐप्लिकेशन को तब डीबग करें, जब तो समस्या ज़्यादा बड़ी हो सकती है, क्योंकि कंपोज़िशन देखकर पता करें कि current वैल्यू कहां दी गई थी. ढूंढें जैसे टूल IDE में या लेआउट इंस्पेक्टर लिखें का इस्तेमाल समस्या को कम करने के लिए किया जा सकता है.

तय करना कि CompositionLocal का इस्तेमाल करना है या नहीं

कुछ ऐसी शर्तें हैं जो CompositionLocal को बेहतर समाधान दे सकती हैं आपके इस्तेमाल के उदाहरण के लिए:

CompositionLocal की डिफ़ॉल्ट वैल्यू अच्छी होनी चाहिए. अगर कोई डिफ़ॉल्ट सेटिंग सेट न हो तो आपको इस बात की गारंटी देनी होगी कि डेवलपर के लिए ऐसी स्थिति में आ जाते हैं, जहां CompositionLocal के लिए कोई वैल्यू नहीं दी जाती है. डिफ़ॉल्ट वैल्यू न देने पर, कॉन्टेंट बनाते समय समस्याएं और निराशा हो सकती है किसी कंपोज़ेबल का टेस्ट करने या उसकी झलक देखने से, जो उस CompositionLocal का इस्तेमाल करता है इसे साफ़ तौर पर दिया जाना ज़रूरी है.

ऐसे कॉन्सेप्ट को इस्तेमाल करने के लिए CompositionLocal का इस्तेमाल न करें जिन्हें पेड़ के स्कोप वाले या सब-हैरारकी के दायरे में उपलब्ध. CompositionLocal समझ में आता है कि यह कब होना चाहिए जिनका इस्तेमाल कुछ ही वंशजों के ज़रिए किया जा सकता है.

अगर इस्तेमाल का आपका उदाहरण इन ज़रूरी शर्तों को पूरा नहीं करता है, तो यहां देखें सेक्शन बनाने से पहले विचार करने के लिए विकल्प CompositionLocal.

गलत तरीके का एक उदाहरण है, ऐसा CompositionLocal बनाना जो आपके मौजूदा किसी खास स्क्रीन का ViewModel, ताकि उस स्क्रीन के सभी कंपोज़ेबल में उपलब्ध कुछ लॉजिक करने के लिए, ViewModel का रेफ़रंस पाएं. यह एक खराब तरीका है क्योंकि किसी खास यूज़र इंटरफ़ेस (यूआई) ट्री के नीचे सभी कंपोज़ेबल को ViewModel. सबसे अच्छा तरीका यह है कि कंपोज़ेबल में सिर्फ़ जानकारी दी जाए उन्हें इस पैटर्न के हिसाब से बनाना होगा कि स्थिति फ़्लो डाउन और इवेंट फ़्लो अप होने पर होती हैं. इस तरीके से, अपने कंपोज़ेबल को ज़्यादा फिर से इस्तेमाल करने लायक और टेस्ट करने में आसान.

CompositionLocal बनाया जा रहा है

CompositionLocal बनाने के लिए दो एपीआई हैं:

  • compositionLocalOf: फिर से बनाने के दौरान दिए गए मान को बदलने से सिर्फ़ अमान्य होता है जिसे पढ़ता है current वैल्यू.

  • staticCompositionLocalOf: compositionLocalOf के उलट, staticCompositionLocalOf के रीड इसे Compose ने ट्रैक किया है. वैल्यू बदलने से content पूरी तरह से बदल जाता है Lambda फ़ंक्शन, जहां CompositionLocal को फिर से लिखने के लिए दिया गया है, न कि सिर्फ़ उन जगहों की जानकारी दें जहां कंपोज़िशन में current वैल्यू पढ़ी गई है.

अगर CompositionLocal को दी गई वैल्यू में बदलाव होने की संभावना बहुत ज़्यादा है या कभी नहीं बदलेगा. परफ़ॉर्मेंस के फ़ायदे पाने के लिए, staticCompositionLocalOf का इस्तेमाल करें.

उदाहरण के लिए, किसी ऐप्लिकेशन के डिज़ाइन सिस्टम की राय, कंपोज़ेबल के हिसाब से दी जा सकती है यूआई कॉम्पोनेंट के लिए शैडो का इस्तेमाल करके हाइलाइट किए जाते हैं. क्योंकि अलग यूज़र इंटरफ़ेस (यूआई) ट्री में, ऐप्लिकेशन की एलिवेशन को पूरे यूज़र इंटरफ़ेस (यूआई) ट्री में देखना चाहिए. हम CompositionLocal. क्योंकि CompositionLocal वैल्यू को कंडिशनल तरीके से लिया जाता है सिस्टम की थीम के हिसाब से, हम compositionLocalOf एपीआई का इस्तेमाल करते हैं:

// 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 कंपोज़ेबल, किसी वैल्यू को CompositionLocal इंस्टेंस के हिसाब से बाइंड करता है हैरारकी है. CompositionLocal को नई वैल्यू देने के लिए, provides इनफ़िक्स फ़ंक्शन का इस्तेमाल करें जो 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
    Card(elevation = LocalElevations.current.card) {
        // Content
    }
}

गौर करने लायक दूसरे विकल्प

इस्तेमाल के कुछ उदाहरणों में, CompositionLocal की ज़रूरत नहीं पड़ती है. अगर आपके इस्तेमाल का उदाहरण तय करना कि इस्तेमाल करना है या नहीं ComposeLocal सेक्शन में कोई दूसरा समाधान बेहतर हो सकता है आपके इस्तेमाल के हिसाब से सही है.

साफ़ तौर पर जानकारी देने वाले पैरामीटर पास करें

कंपोज़ेबल की डिपेंडेंसी के बारे में साफ़ तौर पर जानकारी देना, एक अच्छी आदत है. हमारा सुझाव है कि आप: कि आप कंपोज़ेबल सिर्फ़ उनकी ज़रूरत के हिसाब से पास करें. अलग करने को बढ़ावा देने के लिए और कंपोज़ेबल का दोबारा इस्तेमाल न हो, तो हर कंपोज़ेबल में कम से कम जानकारी दी जा सकती है.

@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 सामग्री lambdas का इस्तेमाल एक जैसे फ़ायदे:

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

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