अपना Compose यूज़र इंटरफ़ेस (यूआई) डिज़ाइन करना

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

var name by remember { mutableStateOf("") }
OutlinedTextField(
    value = name,
    onValueChange = { name = it },
    label = { Text("Name") }
)

कंपोजिट, स्टेटस स्वीकार करते हैं और इवेंट दिखाते हैं. इसलिए, एकतरफ़ा डेटा फ़्लो पैटर्न, Jetpack Compose के साथ अच्छी तरह से काम करता है. इस गाइड में, Compose में एकतरफ़ा डेटा फ़्लो पैटर्न को लागू करने, इवेंट और स्टेटस होल्डर को लागू करने, और Compose में ViewModels के साथ काम करने का तरीका बताया गया है.

एकतरफ़ा डेटा फ़्लो

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

एकतरफ़ा डेटा फ़्लो का इस्तेमाल करने वाले ऐप्लिकेशन के लिए, यूज़र इंटरफ़ेस (यूआई) अपडेट लूप ऐसा दिखता है:

  1. इवेंट: यूआई का कोई हिस्सा एक इवेंट जनरेट करता है और उसे ऊपर की ओर भेजता है. जैसे, बटन पर क्लिक करने पर, ViewModel को हैंडल करने के लिए भेजा गया इवेंट. इसके अलावा, आपके ऐप्लिकेशन की अन्य लेयर से भी इवेंट भेजा जा सकता है. जैसे, उपयोगकर्ता के सेशन की समयसीमा खत्म होने की जानकारी देने वाला इवेंट.
  2. स्टेटस अपडेट करें: इवेंट हैंडलर, स्टेटस बदल सकता है.
  3. स्टेटस दिखाना: स्टेटस होल्डर, स्टेटस को पास करता है और यूज़र इंटरफ़ेस (यूआई) उसे दिखाता है.

इवेंट, यूज़र इंटरफ़ेस (यूआई) से स्टेट होल्डर तक और स्टेट होल्डर से यूआई तक फ़्लो करते हैं.
पहली इमेज. एकतरफ़ा डेटा फ़्लो.

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 और lambda स्वीकार करने वाले कॉम्पोज़ेबल को कई संदर्भों से कॉल किया जा सकता है और इसका बार-बार इस्तेमाल किया जा सकता है. मान लें कि आपके ऐप्लिकेशन के सबसे ऊपर मौजूद ऐप्लिकेशन टास्कबार में हमेशा टेक्स्ट दिखता है और उसमें 'वापस जाएं' बटन होता है. आपके पास एक ऐसा 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
                )
            }
        },
        // ...
    )
}

ViewModels, states, and events: an example

ViewModel और mutableStateOf का इस्तेमाल करके, अपने ऐप्लिकेशन में एकतरफ़ा डेटा फ़्लो भी शुरू किया जा सकता है. ऐसा तब किया जा सकता है, जब इनमें से कोई एक शर्त पूरी हो:

  • आपके यूज़र इंटरफ़ेस (यूआई) की स्थिति, StateFlow या LiveData जैसे स्टेटस होल्डर के ज़रिए दिखाई जाती है.
  • ViewModel, आपके ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) या अन्य लेयर से आने वाले इवेंट को मैनेज करता है और इवेंट के आधार पर स्टेटस होल्डर को अपडेट करता है.

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

स्क्रीन की चार स्थितियां होती हैं:

  • साइन आउट किया गया: जब उपयोगकर्ता ने अभी तक साइन इन नहीं किया है.
  • प्रोसेस जारी है: जब आपका ऐप्लिकेशन, नेटवर्क कॉल करके उपयोगकर्ता को साइन इन करने की कोशिश कर रहा हो.
  • गड़बड़ी: जब साइन इन करते समय कोई गड़बड़ी हुई हो.
  • साइन इन किया गया: जब उपयोगकर्ता साइन इन करता है.

इन स्थितियों को सील की गई क्लास के तौर पर मॉडल किया जा सकता है. 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 के आर्किटेक्चर के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें:

सैंपल