Compose में यूज़र इंटरफ़ेस (यूआई) में बदलाव नहीं किया जा सकता. इसे ड्रॉ करने के बाद, अपडेट नहीं किया जा सकता. आपके पास सिर्फ़ यूज़र इंटरफ़ेस (यूआई) की स्थिति को कंट्रोल करने का विकल्प होता है. यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होने पर, Compose यूआई ट्री के उन हिस्सों को फिर से बनाता है जिनमें बदलाव हुआ है. कंपोज़ेबल, स्टेट स्वीकार कर सकते हैं और इवेंट दिखा सकते हैं. उदाहरण के लिए, TextField वैल्यू स्वीकार करता है और onValueChange कॉलबैक दिखाता है. यह कॉलबैक हैंडलर से वैल्यू बदलने का अनुरोध करता है.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
कंपोज़ेबल, स्टेट स्वीकार करते हैं और इवेंट दिखाते हैं. इसलिए, एकतरफ़ा डेटा फ़्लो पैटर्न, Jetpack Compose के साथ अच्छी तरह से काम करता है. इस गाइड में, Compose में एकतरफ़ा डेटा फ़्लो पैटर्न को लागू करने, इवेंट और स्टेट होल्डर को लागू करने, और Compose में ViewModels के साथ काम करने के तरीके के बारे में बताया गया है.
एकतरफ़ा डेटा फ़्लो
एकतरफ़ा डेटा फ़्लो (यूडीएफ़) एक डिज़ाइन पैटर्न है. इसमें स्टेट नीचे की ओर और इवेंट ऊपर की ओर फ़्लो होते हैं. एकतरफ़ा डेटा फ़्लो का इस्तेमाल करके, यूज़र इंटरफ़ेस (यूआई) में स्टेट दिखाने वाले कंपोज़ेबल को अपने ऐप्लिकेशन के उन हिस्सों से अलग किया जा सकता है जो स्टेट को सेव करते हैं और उसमें बदलाव करते हैं.
एकतरफ़ा डेटा फ़्लो का इस्तेमाल करने वाले ऐप्लिकेशन के लिए, यूज़र इंटरफ़ेस (यूआई) अपडेट लूप इस तरह दिखता है:
- इवेंट: यूज़र इंटरफ़ेस (यूआई) का कोई हिस्सा, इवेंट जनरेट करता है और उसे ऊपर की ओर पास करता है. जैसे, बटन पर क्लिक करने की जानकारी को हैंडल करने के लिए ViewModel को पास किया जाता है. इसके अलावा, इवेंट को आपके ऐप्लिकेशन की अन्य लेयर से पास किया जाता है. जैसे, यह बताने के लिए कि उपयोगकर्ता का सेशन खत्म हो गया है.
- स्टेट अपडेट करना: इवेंट हैंडलर, स्टेट में बदलाव कर सकता है.
- डिसप्ले की स्थिति: स्टेट होल्डर, स्थिति को पास करता है और यूज़र इंटरफ़ेस (यूआई) उसे दिखाता है.
Jetpack Compose का इस्तेमाल करते समय इस पैटर्न को फ़ॉलो करने से कई फ़ायदे मिलते हैं:
- टेस्ट करने की सुविधा: स्टेट को यूज़र इंटरफ़ेस (यूआई) से अलग करने पर, दोनों को अलग-अलग टेस्ट करना आसान हो जाता है.
- स्टेट इनकैप्सुलेशन: स्टेट को सिर्फ़ एक जगह पर अपडेट किया जा सकता है. साथ ही, कंपोज़ेबल की स्थिति के लिए सिर्फ़ एक सोर्स ऑफ़ ट्रुथ होता है. इसलिए, ऐसा कम ही होता है कि अलग-अलग स्थितियों की वजह से बग बन जाएं.
- यूज़र इंटरफ़ेस (यूआई) में एक जैसा अनुभव:
StateFlowयाLiveDataजैसे ऑब्ज़र्वेबल स्टेट होल्डर का इस्तेमाल करके, सभी स्टेट अपडेट को यूज़र इंटरफ़ेस (यूआई) में तुरंत दिखाया जाता है.
Jetpack Compose में एकतरफ़ा डेटा फ़्लो
कंपोज़ेबल, स्टेट और इवेंट के आधार पर काम करते हैं. उदाहरण के लिए, TextField सिर्फ़ तब अपडेट होता है, जब उसका value पैरामीटर अपडेट होता है. साथ ही, यह onValueChange कॉलबैक दिखाता है. यह एक ऐसा इवेंट है जो वैल्यू को नई वैल्यू में बदलने का अनुरोध करता है. Compose, State ऑब्जेक्ट को वैल्यू होल्डर के तौर पर तय करता है. साथ ही, स्टेट वैल्यू में बदलाव होने पर, फिर से कंपोज़िशन ट्रिगर होती है. आपको वैल्यू को कितने समय तक याद रखना है, इसके आधार पर स्टेट को remember { mutableStateOf(value) } या rememberSaveable { mutableStateOf(value) में सेव किया जा सकता है.
TextField कंपोज़ेबल की वैल्यू का टाइप String है. इसलिए, यह वैल्यू कहीं से भी आ सकती है. जैसे, हार्डकोड की गई वैल्यू, ViewModel या पैरंट कंपोज़ेबल से पास की गई वैल्यू. आपको इसे State ऑब्जेक्ट में सेव करने की ज़रूरत नहीं है. हालांकि, onValueChange को कॉल करने पर, आपको वैल्यू अपडेट करनी होगी.
कंपोज़ किए जा सकने वाले पैरामीटर तय करना
कंपोज़ेबल के स्टेट पैरामीटर तय करते समय, इन सवालों के बारे में सोचें:
- कंपोज़ेबल का फिर से इस्तेमाल करना कितना आसान है या उसमें बदलाव करना कितना आसान है?
- स्टेट पैरामीटर, इस कंपोज़ेबल की परफ़ॉर्मेंस पर कैसे असर डालते हैं?
डिकपल करने और फिर से इस्तेमाल करने को बढ़ावा देने के लिए, हर कंपोज़ेबल में कम से कम जानकारी होनी चाहिए. उदाहरण के लिए, किसी समाचार लेख के हेडर को होल्ड करने के लिए कंपोज़ेबल बनाते समय, पूरे समाचार लेख के बजाय सिर्फ़ वह जानकारी पास करें जिसे दिखाना है:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
कभी-कभी, अलग-अलग पैरामीटर का इस्तेमाल करने से भी परफ़ॉर्मेंस बेहतर होती है. उदाहरण के लिए, अगर News में title और subtitle के अलावा ज़्यादा जानकारी शामिल है, तो जब भी News का कोई नया इंस्टेंस Header(news) में पास किया जाता है, तो कंपोज़ेबल फिर से कंपोज़ होगा. भले ही, title और subtitle में कोई बदलाव न हुआ हो.
ध्यान से देखें कि आपने कितने पैरामीटर पास किए हैं. किसी फ़ंक्शन में बहुत ज़्यादा पैरामीटर होने से, फ़ंक्शन के काम करने के तरीके पर असर पड़ता है. इसलिए, इस मामले में उन्हें किसी क्लास में ग्रुप करना बेहतर होता है.
Compose में इवेंट
आपके ऐप्लिकेशन में हर इनपुट को एक इवेंट के तौर पर दिखाया जाना चाहिए: टैप, टेक्स्ट में बदलाव, और यहां तक कि टाइमर या अन्य अपडेट भी. इन इवेंट से आपके यूज़र इंटरफ़ेस (यूआई) की स्थिति बदल जाती है. इसलिए, ViewModel को इन्हें हैंडल करना चाहिए और यूज़र इंटरफ़ेस (यूआई) की स्थिति को अपडेट करना चाहिए.
यूज़र इंटरफ़ेस (यूआई) लेयर को इवेंट हैंडलर के बाहर कभी भी स्थिति नहीं बदलनी चाहिए. ऐसा इसलिए, क्योंकि इससे आपके ऐप्लिकेशन में गड़बड़ियां और समस्याएं आ सकती हैं.
स्टेट और इवेंट हैंडलर लैम्ब्डा के लिए, ऐसी वैल्यू पास करें जिन्हें बदला नहीं जा सकता. इस तरीके के ये फ़ायदे हैं:
- इससे कोड को दोबारा इस्तेमाल करने में आसानी होती है.
- पुष्टि करें कि आपका यूज़र इंटरफ़ेस (यूआई), स्थिति की वैल्यू को सीधे तौर पर नहीं बदलता है.
- आपको एक साथ कई अनुरोध मिलने की वजह से होने वाली समस्याओं का सामना नहीं करना पड़ता, क्योंकि आपने यह पक्का किया है कि किसी अन्य थ्रेड से स्थिति में बदलाव नहीं किया गया है.
- इससे कोड को समझना आसान हो जाता है.
उदाहरण के लिए, String और लैम्डा को पैरामीटर के तौर पर स्वीकार करने वाले कंपोज़ेबल को कई कॉन्टेक्स्ट से कॉल किया जा सकता है. साथ ही, इसे कई बार इस्तेमाल किया जा सकता है. मान लें कि आपके ऐप्लिकेशन में सबसे ऊपर मौजूद ऐप्लिकेशन बार में हमेशा टेक्स्ट दिखता है और उसमें 'वापस जाएं' बटन होता है. ज़्यादा सामान्य MyAppTopAppBar कंपोज़ेबल
को पैरामीटर के तौर पर टेक्स्ट और 'वापस जाएं' बटन का हैंडल मिलता है:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ViewModel, स्टेट, और इवेंट: एक उदाहरण
ViewModel और mutableStateOf का इस्तेमाल करके, अपने ऐप्लिकेशन में एकतरफ़ा डेटा फ़्लो भी लागू किया जा सकता है. इसके लिए, इनमें से कोई एक शर्त पूरी होनी चाहिए:
- आपके यूज़र इंटरफ़ेस (यूआई) की स्थिति को, ऑब्ज़र्वेबल स्टेट होल्डर का इस्तेमाल करके दिखाया जाता है. जैसे,
StateFlowयाLiveData. ViewModel, आपके ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) या अन्य लेयर से आने वाले इवेंट को हैंडल करता है. साथ ही, इवेंट के आधार पर स्टेट होल्डर को अपडेट करता है.
उदाहरण के लिए, साइन-इन स्क्रीन लागू करते समय, साइन इन करें बटन पर टैप करने से, आपके ऐप्लिकेशन को प्रोग्रेस स्पिनर और नेटवर्क कॉल दिखाना चाहिए. अगर लॉगिन हो जाता है, तो आपका ऐप्लिकेशन किसी दूसरी स्क्रीन पर चला जाता है. अगर कोई गड़बड़ी होती है, तो ऐप्लिकेशन एक स्नैकबार दिखाता है. यहां स्क्रीन की स्थिति और इवेंट को मॉडल करने का तरीका बताया गया है:
स्क्रीन की चार स्थितियां होती हैं:
- साइन आउट किया गया: जब उपयोगकर्ता ने अब तक साइन इन नहीं किया है.
- प्रोसेस जारी है: जब आपका ऐप्लिकेशन, नेटवर्क कॉल करके उपयोगकर्ता को साइन इन कराने की कोशिश कर रहा हो.
- Error: साइन इन करते समय कोई गड़बड़ी हुई.
- साइन इन किया गया: जब उपयोगकर्ता ने साइन इन किया हो.
इन स्थितियों को सील की गई क्लास के तौर पर मॉडल किया जा सकता है. ViewModel, स्थिति को State के तौर पर दिखाता है, शुरुआती स्थिति सेट करता है, और ज़रूरत के हिसाब से स्थिति को अपडेट करता है. ViewModel, साइन-इन इवेंट को भी मैनेज करता है. इसके लिए, यह onSignIn() तरीके का इस्तेमाल करता है.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
mutableStateOf API के अलावा, Compose LiveData, Flow, और Observable के लिए एक्सटेंशन उपलब्ध कराता है, ताकि इन्हें लिसनर के तौर पर रजिस्टर किया जा सके और वैल्यू को स्टेट के तौर पर दिखाया जा सके.
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
ज़्यादा जानें
Jetpack Compose में आर्किटेक्चर के बारे में ज़्यादा जानने के लिए, इन संसाधनों को देखें:
सैंपल
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- स्टेट और Jetpack Compose
- Compose में यूज़र इंटरफ़ेस (यूआई) की स्थिति सेव करना
- उपयोगकर्ता के इनपुट को मैनेज करना