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
का इस्तेमाल, Material थीम में किया जाता है.
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
इंस्टेंस का दायरा, कॉम्पोज़िशन के किसी हिस्से तक सीमित होता है, ताकि आप ट्री के अलग-अलग लेवल पर अलग-अलग वैल्यू दे सकें. current
वैल्यू
का मान CompositionLocal
कंपोज़िशन के उस हिस्से में पूर्वज.
CompositionLocal
को नई वैल्यू देने के लिए, CompositionLocalProvider
और उसके provides
इनफ़िक्स फ़ंक्शन का इस्तेमाल करें. यह फ़ंक्शन, CompositionLocal
कुंजी को value
से जोड़ता है. CompositionLocal
की current
प्रॉपर्टी को ऐक्सेस करते समय, CompositionLocalProvider
के content
लैम्ब्डा फ़ंक्शन को दी गई वैल्यू मिलेगी. जब कोई नई वैल्यू दी जाती है, तो Compose, 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
कॉम्पोज़ेबल की झलक.
पिछले उदाहरण में, Material composables ने 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
का इस्तेमाल करने का एक और अहम सिग्नल यह है कि पैरामीटर
लागू करने की क्रॉस-कटिंग और इंटरमीडिएट लेयर्स को पता नहीं होना चाहिए
यह मौजूद है, क्योंकि उन इंटरमीडिएट लेयर को जानकारी देने से,
कंपोज़ेबल का इस्तेमाल कर रही हूँ. उदाहरण के लिए, Android की अनुमतियों के लिए क्वेरी करना
CompositionLocal
की ओर से उपलब्ध कराया जाता है. मीडिया पिकर का कॉम्पोनेंट, डिवाइस पर अनुमति से सुरक्षित कॉन्टेंट को ऐक्सेस करने के लिए नई सुविधा जोड़ सकता है. इसके लिए, उसे एपीआई में बदलाव करने की ज़रूरत नहीं होती. साथ ही, मीडिया पिकर को कॉल करने वाले को, एलिमेंट के लिए इस्तेमाल किए गए इस नए कॉन्टेक्स्ट के बारे में पता होना चाहिए.
हालांकि, CompositionLocal
हमेशा सबसे सही समाधान नहीं होता. बुध
CompositionLocal
के ज़्यादा इस्तेमाल से बचने की कोशिश करें, क्योंकि इसके कुछ नकारात्मक पहलू भी हैं:
CompositionLocal
की वजह से किसी कंपोज़ेबल के काम करने के तरीके के बारे में सोच-विचार करना मुश्किल हो जाता है. ये वैल्यू, डिफ़ॉल्ट रूप से डिपेंडेंसी बनाती हैं. इसलिए, इनका इस्तेमाल करने वाले कॉम्पोज़ेबल को यह पक्का करना होगा कि हर CompositionLocal
के लिए वैल्यू दी गई हो.
इसके अलावा, हो सकता है कि इस डिपेंडेंसी के लिए कोई सटीक सोर्स न हो, क्योंकि यह कॉम्पोज़िशन के किसी भी हिस्से में बदल सकती है. इसलिए, किसी समस्या के होने पर ऐप्लिकेशन को डीबग करना ज़्यादा मुश्किल हो सकता है. ऐसा इसलिए, क्योंकि आपको यह देखने के लिए कॉम्पोज़िशन में ऊपर जाना होगा कि current
वैल्यू कहां दी गई थी. ढूंढें जैसे टूल
IDE में या लेआउट इंस्पेक्टर लिखें का इस्तेमाल
समस्या को कम करने के लिए किया जा सकता है.
CompositionLocal
का इस्तेमाल करना है या नहीं, यह तय करना
कुछ स्थितियों में, CompositionLocal
आपके इस्तेमाल के उदाहरण के लिए एक अच्छा समाधान हो सकता है:
CompositionLocal
की डिफ़ॉल्ट वैल्यू अच्छी होनी चाहिए. अगर कोई डिफ़ॉल्ट सेटिंग सेट न हो
तो आपको इस बात की गारंटी देनी होगी कि डेवलपर के लिए
ऐसी स्थिति में आ जाते हैं, जहां CompositionLocal
के लिए कोई वैल्यू नहीं दी जाती है.
डिफ़ॉल्ट वैल्यू न देने पर, जांच करते समय या किसी ऐसे कॉम्पोज़ेबल की झलक देखते समय समस्याएं आ सकती हैं जिसमें CompositionLocal
का इस्तेमाल किया गया हो. इसके लिए, CompositionLocal
की वैल्यू साफ़ तौर पर देना ज़रूरी होगा.
उन कॉन्सेप्ट के लिए CompositionLocal
का इस्तेमाल न करें जिन्हें ट्री-स्कोप या सब-हियरेकी के दायरे में नहीं माना जाता. CompositionLocal
समझ में आता है कि यह कब होना चाहिए
जिनका इस्तेमाल कुछ ही वंशजों के ज़रिए किया जा सकता है.
अगर आपका इस्तेमाल का उदाहरण इन ज़रूरी शर्तों को पूरा नहीं करता है, तो CompositionLocal
बनाने से पहले, विकल्पों पर विचार करें सेक्शन देखें.
गलत तरीके का एक उदाहरण, ऐसी CompositionLocal
बनाना है जिसमें किसी खास स्क्रीन का ViewModel
हो, ताकि उस स्क्रीन के सभी कॉम्पोज़ेबल को कोई लॉजिक लागू करने के लिए ViewModel
का रेफ़रंस मिल सके. यह एक गलत तरीका है, क्योंकि किसी खास यूज़र इंटरफ़ेस (यूआई) ट्री के नीचे मौजूद सभी कॉम्पोज़ेबल को ViewModel
के बारे में जानने की ज़रूरत नहीं होती. सबसे सही तरीका यह है कि कॉम्पोज़ेबल में सिर्फ़ वह जानकारी दें जो ज़रूरी है. इसके लिए, स्टेटस नीचे की ओर और इवेंट ऊपर की ओर के पैटर्न का पालन करें. इस तरीके से, आपके कॉम्पोज़ेबल को फिर से इस्तेमाल करना आसान हो जाएगा और उनकी जांच करना भी आसान हो जाएगा.
CompositionLocal
बनाया जा रहा है
CompositionLocal
बनाने के लिए दो एपीआई हैं:
compositionLocalOf
: फिर से बनाने के दौरान दिए गए मान को बदलने से सिर्फ़ अमान्य होता है जिसे पढ़ता हैcurrent
वैल्यू.staticCompositionLocalOf
:compositionLocalOf
के उलट, Compose मेंstaticCompositionLocalOf
के रीड को ट्रैक नहीं किया जाता. वैल्यू बदलने पर,content
के पूरे लेम्ब्डा में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
composable, दी गई हैरारकी के लिए 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 MyCard(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
कॉन्टेंट लैम्ब्डा का इस्तेमाल भी उसी तरह किया जा सकता है, ताकि वही फ़ायदे मिल सकें:
@Composable fun MyComposable(myViewModel: MyViewModel = viewModel()) { // ... ReusablePartOfTheScreen( content = { Button( onClick = { myViewModel.loadData() } ) { Text("Confirm") } } ) } @Composable fun ReusablePartOfTheScreen(content: @Composable () -> Unit) { Column { // ... content() } }
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- Compose में थीम की संरचना
- Compose में व्यू का इस्तेमाल करना
- Jetpack Compose के लिए Kotlin