Compose ऐप्लिकेशन में, UI state को कहां होस्ट करना है, यह इस बात पर निर्भर करता है कि UI लॉजिक या कारोबार के लॉजिक को इसकी ज़रूरत है या नहीं. इस दस्तावेज़ में, इन दो मुख्य स्थितियों के बारे में बताया गया है.
सबसे सही तरीका
आपको यूज़र इंटरफ़ेस (यूआई) की स्थिति को उन सभी कंपोज़ेबल के बीच सबसे निचले सामान्य पूर्वज तक ले जाना चाहिए जो इसे पढ़ते और लिखते हैं. आपको उस स्टेट को उस कॉम्पोनेंट के सबसे करीब रखना चाहिए जहां उसका इस्तेमाल किया जाता है. स्टेट के मालिक से, उपभोक्ताओं को ऐसी स्थिति और इवेंट के बारे में जानकारी मिलती है जिनमें बदलाव नहीं किया जा सकता. इससे वे स्थिति में बदलाव कर पाते हैं.
सबसे कम सामान्य पूर्वज, कंपोज़िशन से बाहर भी हो सकता है. उदाहरण के लिए,
जब कारोबारी नियमों के शामिल होने की वजह से, ViewModel में स्टेट को ऊपर ले जाया जाता है.
इस पेज पर, इस सबसे सही तरीके के बारे में ज़्यादा जानकारी दी गई है. साथ ही, एक चेतावनी भी दी गई है जिसे ध्यान में रखना ज़रूरी है.
यूज़र इंटरफ़ेस (यूआई) की स्थिति और यूज़र इंटरफ़ेस (यूआई) लॉजिक के टाइप
यहां यूज़र इंटरफ़ेस (यूआई) की स्थिति और लॉजिक के टाइप की परिभाषाएं दी गई हैं. इनका इस्तेमाल इस दस्तावेज़ में किया गया है.
यूज़र इंटरफ़ेस (यूआई) की स्थिति
यूज़र इंटरफ़ेस (यूआई) की स्थिति वह प्रॉपर्टी है जो यूज़र इंटरफ़ेस (यूआई) के बारे में बताती है. यूज़र इंटरफ़ेस (यूआई) की स्थिति दो तरह की होती है:
- स्क्रीन यूज़र इंटरफ़ेस (यूआई) की स्थिति वह जानकारी होती है जिसे आपको स्क्रीन पर दिखाना होता है. उदाहरण के लिए,
NewsUiStateक्लास में, खबरों के लेख और यूज़र इंटरफ़ेस (यूआई) को रेंडर करने के लिए ज़रूरी अन्य जानकारी शामिल हो सकती है. आम तौर पर, यह स्थिति क्रम की अन्य लेयर से जुड़ी होती है, क्योंकि इसमें ऐप्लिकेशन का डेटा होता है. - यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति से मतलब, यूज़र इंटरफ़ेस (यूआई) एलिमेंट की उन प्रॉपर्टी से है जो यह तय करती हैं कि उन्हें कैसे रेंडर किया जाएगा. यूज़र इंटरफ़ेस (यूआई) एलिमेंट को दिखाया या छिपाया जा सकता है. साथ ही, इसमें कोई फ़ॉन्ट, फ़ॉन्ट साइज़ या फ़ॉन्ट का रंग हो सकता है. Jetpack Compose में, स्टेट कंपोज़ेबल से बाहर होती है. इसे कंपोज़ेबल के आस-पास से हटाकर, कंपोज़ेबल फ़ंक्शन को कॉल करने वाले या स्टेट होल्डर में भी ले जाया जा सकता है. इसका एक उदाहरण,
Scaffoldकंपोज़ेबल के लिएScaffoldStateहै.
लॉजिक
किसी ऐप्लिकेशन में लॉजिक, कारोबारी नियम या यूज़र इंटरफ़ेस (यूआई) लॉजिक हो सकता है:
- कारोबारी नियम, ऐप्लिकेशन के डेटा के लिए प्रॉडक्ट की ज़रूरी शर्तों को लागू करने की प्रोसेस है. उदाहरण के लिए, जब कोई उपयोगकर्ता बटन पर टैप करता है, तब किसी समाचार पढ़ने वाले ऐप्लिकेशन में किसी लेख को बुकमार्क करना. किसी फ़ाइल या डेटाबेस में बुकमार्क सेव करने का यह लॉजिक, आम तौर पर डोमेन या डेटा लेयर में रखा जाता है. आम तौर पर, स्टेट होल्डर इस लॉजिक को उन लेयर को सौंपता है जो तरीके उपलब्ध कराती हैं.
- यूज़र इंटरफ़ेस (यूआई) लॉजिक का संबंध, स्क्रीन पर यूज़र इंटरफ़ेस (यूआई) की स्थिति को कैसे दिखाया जाए, इससे होता है. उदाहरण के लिए, जब उपयोगकर्ता कोई कैटगरी चुनता है, तो उसे सही खोज बार का सुझाव मिलता है. इसके अलावा, सूची में किसी खास आइटम पर स्क्रोल करना या जब उपयोगकर्ता किसी बटन पर क्लिक करता है, तो उसे किसी खास स्क्रीन पर ले जाने का नेविगेशन लॉजिक.
यूज़र इंटरफ़ेस (यूआई) का लॉजिक
जब यूज़र इंटरफ़ेस (यूआई) लॉजिक को स्थिति को पढ़ना या लिखना होता है, तब आपको यूज़र इंटरफ़ेस (यूआई) के लिए स्थिति को स्कोप करना चाहिए. इसके लिए, आपको इसके लाइफ़साइकल का पालन करना होगा. इसके लिए, आपको कंपोज़ेबल फ़ंक्शन में सही लेवल पर स्टेट को ऊपर ले जाना चाहिए. इसके अलावा, इसे प्लेन स्टेट होल्डर क्लास में भी किया जा सकता है. इसे यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल के हिसाब से भी स्कोप किया जाता है.
यहां दोनों समाधानों के बारे में बताया गया है. साथ ही, यह भी बताया गया है कि किस समाधान का इस्तेमाल कब करना चाहिए.
स्टेट के मालिक के तौर पर कंपोज़ेबल
अगर स्थिति और लॉजिक आसान हैं, तो कंपोज़ेबल में यूज़र इंटरफ़ेस (यूआई) लॉजिक और यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति रखना एक अच्छा तरीका है. ज़रूरत के मुताबिक, अपने इंटरनल स्टेट को कंपोज़ेबल या होइस्ट किया जा सकता है.
स्टेट को ऊपर ले जाने की ज़रूरत नहीं है
स्टेट को हमेशा ऊपर ले जाना ज़रूरी नहीं होता. जब किसी अन्य कंपोज़ेबल को स्टेट को कंट्रोल करने की ज़रूरत नहीं होती है, तब कंपोज़ेबल में स्टेट को इंटरनल रखा जा सकता है. इस स्निपेट में, एक कंपोज़ेबल है जो टैप करने पर बड़ा और छोटा होता है:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } // Define the UI element expanded state Text( text = AnnotatedString(message.content), modifier = Modifier.clickable { showDetails = !showDetails // Apply UI logic } ) if (showDetails) { Text(message.timestamp) } }
वैरिएबल showDetails, इस यूज़र इंटरफ़ेस (यूआई) एलिमेंट की इंटरनल स्टेट है. इसे सिर्फ़ इस कंपोज़ेबल में पढ़ा और बदला जाता है. साथ ही, इस पर लागू होने वाला लॉजिक बहुत आसान है.
इसलिए, इस मामले में स्टेट को ऊपर ले जाने से ज़्यादा फ़ायदा नहीं होगा. इसलिए, इसे इंटरनल छोड़ा जा सकता है. ऐसा करने से, यह कंपोज़ेबल, एक्सपैंड की गई स्थिति का मालिक और भरोसेमंद स्रोत बन जाता है.
कंपोज़ेबल में लिफ़्ट करना
अगर आपको अपने यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को अन्य कंपोज़ेबल के साथ शेयर करना है और अलग-अलग जगहों पर यूआई लॉजिक लागू करना है, तो यूआई के क्रम में इसे ऊपर की ओर ले जाएं. इससे आपके कंपोज़ेबल, ज़्यादा बार इस्तेमाल किए जा सकते हैं और उनकी जांच करना भी आसान हो जाता है.
यहां दिए गए उदाहरण में, एक चैट ऐप्लिकेशन दिखाया गया है. इसमें दो सुविधाएं लागू की गई हैं:
JumpToBottomबटन, मैसेज की सूची को सबसे नीचे तक स्क्रोल करता है. यह बटन, सूची की स्थिति के हिसाब से यूज़र इंटरफ़ेस (यूआई) लॉजिक को लागू करता है.- उपयोगकर्ता के नए मैसेज भेजने के बाद,
MessagesListसूची सबसे नीचे स्क्रोल हो जाती है. UserInput, सूची की स्थिति पर यूज़र इंटरफ़ेस (यूआई) लॉजिक को लागू करता है.
JumpToBottom बटन और नए मैसेज पर सबसे नीचे तक स्क्रोल करेंकंपोज़ेबल हैरारकी इस तरह से होती है:
LazyColumn स्टेट को बातचीत वाली स्क्रीन पर ले जाया जाता है, ताकि ऐप्लिकेशन यूज़र इंटरफ़ेस (यूआई) लॉजिक को लागू कर सके. साथ ही, उन सभी कंपोज़ेबल से स्टेट को पढ़ सके जिनके लिए इसकी ज़रूरत होती है:
LazyColumn से ConversationScreenLazyColumn की स्थिति को ऊपर ले जाना
इसलिए, आखिर में कंपोज़ेबल ये हैं:
LazyListState को ConversationScreenकोड यहां दिया गया है:
@Composable private fun ConversationScreen(/*...*/) { val scope = rememberCoroutineScope() val lazyListState = rememberLazyListState() // State hoisted to the ConversationScreen MessagesList(messages, lazyListState) // Reuse same state in MessageList UserInput( onMessageSent = { // Apply UI logic to lazyListState scope.launch { lazyListState.scrollToItem(0) } }, ) } @Composable private fun MessagesList( messages: List<Message>, lazyListState: LazyListState = rememberLazyListState() // LazyListState has a default value ) { LazyColumn( state = lazyListState // Pass hoisted state to LazyColumn ) { items(messages, key = { message -> message.id }) { item -> Message(/*...*/) } } val scope = rememberCoroutineScope() JumpToBottom(onClicked = { scope.launch { lazyListState.scrollToItem(0) // UI logic being applied to lazyListState } }) }
LazyListState को यूज़र इंटरफ़ेस (यूआई) लॉजिक के लिए, ज़रूरत के हिसाब से ऊपर ले जाया जाता है. इसे कंपोज़ेबल फ़ंक्शन में शुरू किया जाता है. इसलिए, इसे कंपोज़िशन में सेव किया जाता है. यह इसके लाइफ़साइकल के हिसाब से काम करता है.
ध्यान दें कि lazyListState को MessagesList तरीके में तय किया गया है. इसकी डिफ़ॉल्ट वैल्यू rememberLazyListState() है. Compose में यह एक सामान्य पैटर्न है.
इससे कंपोज़ेबल को फिर से इस्तेमाल करना और उनमें बदलाव करना आसान हो जाता है. इसके बाद, ऐप्लिकेशन के अलग-अलग हिस्सों में कंपोज़ेबल का इस्तेमाल किया जा सकता है. ऐसा हो सकता है कि इन हिस्सों को स्थिति को कंट्रोल करने की ज़रूरत न हो. आम तौर पर, कंपोज़ेबल की टेस्टिंग या झलक देखने के दौरान ऐसा होता है. LazyColumn अपनी स्थिति को ठीक इसी तरह से तय करता है.
LazyListState के लिए सबसे कम कॉमन ऐनसेस्टर ConversationScreenस्टेट होल्डर क्लास को स्टेट ओनर के तौर पर इस्तेमाल करना
जब किसी कंपोज़ेबल में यूज़र इंटरफ़ेस (यूआई) का ऐसा लॉजिक शामिल हो जिसमें यूज़र इंटरफ़ेस (यूआई) एलिमेंट के एक या उससे ज़्यादा स्टेट फ़ील्ड शामिल हों, तो उसे यह ज़िम्मेदारी स्टेट होल्डर को सौंपनी चाहिए. जैसे, सामान्य स्टेट होल्डर क्लास. इससे कंपोज़ेबल के लॉजिक को अलग से टेस्ट करना आसान हो जाता है. साथ ही, इसकी जटिलता कम हो जाती है. यह तरीका, ज़िम्मेदारियों को अलग-अलग करने के सिद्धांत के मुताबिक है: कंपोज़ेबल, यूज़र इंटरफ़ेस (यूआई) एलिमेंट को दिखाने के लिए ज़िम्मेदार होता है. वहीं, स्टेट होल्डर में यूज़र इंटरफ़ेस (यूआई) का लॉजिक और यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति शामिल होती है.
प्लेन स्टेट होल्डर क्लास, आपके कंपोज़ेबल फ़ंक्शन को कॉल करने वालों के लिए आसान फ़ंक्शन उपलब्ध कराती हैं, ताकि उन्हें यह लॉजिक खुद न लिखना पड़े.
इन सामान्य क्लास को कंपोज़िशन में बनाया और सेव किया जाता है. ये कंपोज़ेबल के लाइफ़साइकल को फ़ॉलो करते हैं. इसलिए, ये Compose लाइब्रेरी से मिले टाइप का इस्तेमाल कर सकते हैं. जैसे, rememberNavController() या rememberLazyListState().
इसका एक उदाहरण, LazyListState प्लेन स्टेट होल्डर क्लास है. इसे Compose में लागू किया जाता है, ताकि LazyColumn या LazyRow के यूज़र इंटरफ़ेस (यूआई) की जटिलता को कंट्रोल किया जा सके.
// LazyListState.kt @Stable class LazyListState constructor( firstVisibleItemIndex: Int = 0, firstVisibleItemScrollOffset: Int = 0 ) : ScrollableState { /** * The holder class for the current scroll position. */ private val scrollPosition = LazyListScrollPosition( firstVisibleItemIndex, firstVisibleItemScrollOffset ) suspend fun scrollToItem(/*...*/) { /*...*/ } override suspend fun scroll() { /*...*/ } suspend fun animateScrollToItem() { /*...*/ } }
LazyListState, LazyColumn की स्थिति को इनकैप्सुलेट करता है. साथ ही, इस यूज़र इंटरफ़ेस (यूआई) एलिमेंट के लिए scrollPosition को सेव करता है. यह स्क्रोल करने की पोज़िशन में बदलाव करने के तरीके भी दिखाता है. उदाहरण के लिए, किसी आइटम पर स्क्रोल करना.
जैसा कि आप देख सकते हैं, कंपोज़ेबल की ज़िम्मेदारियां बढ़ाने से, स्टेट होल्डर की ज़रूरत बढ़ जाती है. ज़िम्मेदारियां, यूज़र इंटरफ़ेस (यूआई) लॉजिक में हो सकती हैं या सिर्फ़ ट्रैक किए जाने वाले स्टेट की संख्या में हो सकती हैं.
एक और सामान्य पैटर्न यह है कि ऐप्लिकेशन में रूट कंपोज़ेबल फ़ंक्शन की जटिलता को मैनेज करने के लिए, सामान्य स्टेट होल्डर क्लास का इस्तेमाल किया जाता है. इस तरह की क्लास का इस्तेमाल, ऐप्लिकेशन-लेवल की स्थिति को इनकैप्सुलेट करने के लिए किया जा सकता है. जैसे, नेविगेशन की स्थिति और स्क्रीन का साइज़. इसके बारे में पूरी जानकारी, यूज़र इंटरफ़ेस (यूआई) लॉजिक और इसके स्टेट होल्डर पेज पर देखी जा सकती है.
कारोबारी नियम
अगर कंपोज़ेबल और सामान्य स्टेट होल्डर क्लास, यूज़र इंटरफ़ेस (यूआई) लॉजिक और यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को मैनेज करती हैं, तो स्क्रीन लेवल का स्टेट होल्डर ये काम करता है:
- ऐप्लिकेशन के कारोबार के लॉजिक का ऐक्सेस देना. यह आम तौर पर, क्रम-व्यवस्था की अन्य लेयर में रखा जाता है. जैसे, कारोबार और डेटा लेयर.
- किसी स्क्रीन पर दिखाने के लिए ऐप्लिकेशन का डेटा तैयार करना. इससे स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति तय होती है.
ViewModel को स्टेट के मालिक के तौर पर इस्तेमाल करना
Android डेवलपमेंट में AAC ViewModels के फ़ायदों की वजह से, ये कारोबार के लॉजिक का ऐक्सेस देने और स्क्रीन पर प्रज़ेंटेशन के लिए ऐप्लिकेशन डेटा तैयार करने के लिए सही हैं.
ViewModel में यूज़र इंटरफ़ेस (यूआई) की स्थिति को ऊपर ले जाने का मतलब है कि उसे कंपोज़िशन से बाहर ले जाना.
ViewModel में भेजा गया स्टेट, कंपोज़िशन से बाहर सेव किया जाता है.ViewModel को कंपोज़िशन के हिस्से के तौर पर सेव नहीं किया जाता. इन्हें फ़्रेमवर्क उपलब्ध कराता है. साथ ही, इन्हें ViewModelStoreOwner के दायरे में रखा जाता है. यह ViewModelStoreOwner कोई ऐक्टिविटी, फ़्रैगमेंट, नेविगेशन ग्राफ़ या नेविगेशन ग्राफ़ का डेस्टिनेशन हो सकता है. ViewModel स्कोप के बारे में ज़्यादा जानने के लिए, दस्तावेज़ पढ़ें.
इसके बाद, ViewModel यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए, भरोसेमंद सोर्स और सबसे सामान्य पूर्वज होता है.
स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति
ऊपर दी गई परिभाषाओं के मुताबिक, स्क्रीन यूज़र इंटरफ़ेस (यूआई) की स्थिति, कारोबार के नियमों को लागू करके तय की जाती है. स्क्रीन लेवल के स्टेट होल्डर की यह ज़िम्मेदारी होती है. इसका मतलब है कि स्क्रीन यूज़र इंटरफ़ेस (यूआई) स्टेट को आम तौर पर स्क्रीन लेवल के स्टेट होल्डर में होस्ट किया जाता है. इस मामले में, यह ViewModel है.
चैट ऐप्लिकेशन के ConversationViewModel और स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति और इवेंट को बदलने के लिए, इसे कैसे दिखाया जाता है, इस पर विचार करें:
class ConversationViewModel( channelId: String, messagesRepository: MessagesRepository ) : ViewModel() { val messages = messagesRepository .getLatestMessages(channelId) .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = emptyList() ) // Business logic fun sendMessage(message: Message) { /* ... */ } }
कंपोज़ेबल, ViewModel में होस्ट की गई स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति का इस्तेमाल करते हैं. आपको स्क्रीन-लेवल के कंपोज़ेबल में ViewModel इंस्टेंस को इंजेक्ट करना चाहिए, ताकि कारोबारी नियमों को ऐक्सेस किया जा सके.
यहाँ स्क्रीन-लेवल के कंपोज़ेबल में इस्तेमाल किए गए ViewModel का उदाहरण दिया गया है.
यहां, कंपोज़ेबल ConversationScreen(), ViewModel में होस्ट की गई स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति का इस्तेमाल करता है:
@Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val messages by conversationViewModel.messages.collectAsStateWithLifecycle() ConversationScreen( messages = messages, onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) } ) } @Composable private fun ConversationScreen( messages: List<Message>, onSendMessage: (Message) -> Unit ) { MessagesList(messages, onSendMessage) /* ... */ }
प्रॉपर्टी ड्रिलिंग
“प्रॉपर्टी ड्रिलिंग” का मतलब है कि डेटा को कई नेस्ट किए गए चाइल्ड कॉम्पोनेंट से उस जगह पर पास करना जहाँ उन्हें पढ़ा जाता है.
Compose में प्रॉपर्टी ड्रिलिंग कहां दिख सकती है, इसका एक सामान्य उदाहरण यह है कि जब स्क्रीन लेवल के स्टेट होल्डर को टॉप लेवल पर इंजेक्ट किया जाता है और स्टेट और इवेंट को चाइल्ड कंपोज़ेबल में पास किया जाता है. इससे कंपोज़ेबल फ़ंक्शन के सिग्नेचर भी ज़्यादा जनरेट हो सकते हैं.
इवेंट को अलग-अलग लैम्ब्डा पैरामीटर के तौर पर दिखाने से, फ़ंक्शन सिग्नेचर पर ज़्यादा असर पड़ सकता है. हालांकि, इससे यह पता चलता है कि कंपोज़ेबल फ़ंक्शन की ज़िम्मेदारियां क्या हैं. इससे आपको एक नज़र में पता चल जाता है कि यह क्या करता है.
स्टेट और इवेंट को एक जगह पर कैप्सूल करने के लिए, रैपर क्लास बनाने के बजाय प्रॉपर्टी ड्रिलिंग का इस्तेमाल करना बेहतर होता है. ऐसा इसलिए, क्योंकि इससे कंपोज़ेबल की ज़िम्मेदारियों के दिखने की संभावना कम हो जाती है. रैपर क्लास न होने की वजह से, कंपोज़ेबल फ़ंक्शन को सिर्फ़ ज़रूरी पैरामीटर पास किए जाते हैं. यह सबसे सही तरीका है.
अगर ये इवेंट नेविगेशन इवेंट हैं, तो भी सबसे सही तरीका यही है. इसके बारे में ज़्यादा जानने के लिए, नेविगेशन से जुड़े दस्तावेज़ पढ़ें.
अगर आपको परफ़ॉर्मेंस से जुड़ी कोई समस्या दिखती है, तो आपके पास स्थिति को पढ़ने की प्रोसेस को कुछ समय के लिए रोकने का विकल्प भी होता है. ज़्यादा जानने के लिए, परफ़ॉर्मेंस से जुड़े दस्तावेज़ देखें.
यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति
अगर कोई ऐसा बिज़नेस लॉजिक है जिसे यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को पढ़ना या उसमें बदलाव करना है, तो यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को स्क्रीन लेवल के स्टेट होल्डर पर ले जाया जा सकता है.
चैट ऐप्लिकेशन के उदाहरण को आगे बढ़ाते हुए, ऐप्लिकेशन किसी ग्रुप चैट में उपयोगकर्ता को सुझाव दिखाता है. ऐसा तब होता है, जब उपयोगकर्ता @ और कोई हिंट टाइप करता है. ये सुझाव, डेटा लेयर से मिलते हैं. साथ ही, उपयोगकर्ता के सुझावों की सूची का हिसाब लगाने के लॉजिक को कारोबार से जुड़ा लॉजिक माना जाता है. यह सुविधा ऐसी दिखती है:
@ और हिंटइस सुविधा को लागू करने वाला ViewModel इस तरह दिखेगा:
class ConversationViewModel(/*...*/) : ViewModel() { // Hoisted state var inputMessage by mutableStateOf("") private set val suggestions: StateFlow<List<Suggestion>> = snapshotFlow { inputMessage } .filter { hasSocialHandleHint(it) } .mapLatest { getHandle(it) } .mapLatest { repository.getSuggestions(it) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = emptyList() ) fun updateInput(newInput: String) { inputMessage = newInput } }
inputMessage एक वैरिएबल है, जो TextField की स्थिति को सेव करता है. जब भी उपयोगकर्ता नया इनपुट टाइप करता है, तो ऐप्लिकेशन, कारोबारी नियमों को कॉल करता है, ताकि suggestions जनरेट किया जा सके.
suggestions, स्क्रीन यूज़र इंटरफ़ेस (यूआई) की स्थिति है. इसका इस्तेमाल Compose यूज़र इंटरफ़ेस (यूआई) करता है. इसके लिए, वह StateFlow से डेटा इकट्ठा करता है.
चेतावनी
Compose के कुछ यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति के लिए, ViewModel में होस्ट करने के लिए खास बातों का ध्यान रखना पड़ सकता है. उदाहरण के लिए, Compose यूज़र इंटरफ़ेस (यूआई) एलिमेंट के कुछ स्टेट होल्डर, स्टेट में बदलाव करने के लिए तरीके उपलब्ध कराते हैं. इनमें से कुछ ऐसे फ़ंक्शन हो सकते हैं जो निलंबित किए गए हों और ऐनिमेशन ट्रिगर करते हों. अगर कंपोज़िशन के स्कोप से बाहर के CoroutineScope से इन सस्पेंड फ़ंक्शन को कॉल किया जाता है, तो ये अपवाद थ्रो कर सकते हैं.
मान लें कि ऐप्लिकेशन ड्रॉअर का कॉन्टेंट डाइनैमिक है और बंद होने के बाद, आपको डेटा लेयर से इसे फ़ेच और रीफ़्रेश करना है. आपको ड्रॉअर की स्थिति को ViewModel पर ले जाना चाहिए, ताकि इस एलिमेंट पर यूज़र इंटरफ़ेस (यूआई) और कारोबारी नियम, दोनों को स्टेट के मालिक से कॉल किया जा सके.
हालांकि, Compose UI से viewModelScope का इस्तेमाल करके, DrawerState के close() तरीके को कॉल करने पर, IllegalStateException टाइप का रनटाइम अपवाद होता है. इसमें यह मैसेज दिखता है: “इस CoroutineContext” में MonotonicFrameClock उपलब्ध नहीं है.
इस समस्या को ठीक करने के लिए, कंपोज़िशन के स्कोप में मौजूद CoroutineScope का इस्तेमाल करें. यह CoroutineContext में एक MonotonicFrameClock उपलब्ध कराता है, जो सस्पेंड फ़ंक्शन के काम करने के लिए ज़रूरी है.
इस क्रैश को ठीक करने के लिए, CoroutineContext में मौजूद को-रूटीन के ViewModel को कंपोज़िशन के स्कोप वाले ViewModel पर स्विच करें. यह इस तरह दिख सकता है:
class ConversationViewModel(/*...*/) : ViewModel() { val drawerState = DrawerState(initialValue = DrawerValue.Closed) private val _drawerContent = MutableStateFlow(DrawerContent.Empty) val drawerContent: StateFlow<DrawerContent> = _drawerContent.asStateFlow() fun closeDrawer(uiScope: CoroutineScope) { viewModelScope.launch { withContext(uiScope.coroutineContext) { // Use instead of the default context drawerState.close() } // Fetch drawer content and update state _drawerContent.update { content } } } } // in Compose @Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val scope = rememberCoroutineScope() ConversationScreen(onCloseDrawer = { conversationViewModel.closeDrawer(uiScope = scope) }) }
ज़्यादा जानें
स्टेट और Jetpack Compose के बारे में ज़्यादा जानने के लिए, यहां दिए गए अतिरिक्त संसाधन देखें.
सैंपल
कोडलैब
वीडियो
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक का टेक्स्ट दिखता है
- Compose में यूज़र इंटरफ़ेस (यूआई) की स्थिति सेव करना
- सूचियां और ग्रिड
- Compose यूज़र इंटरफ़ेस (यूआई) का आर्किटेक्चर तैयार करना