Jetpack Compose की मदद से ज़रूरत के हिसाब से ऐप्लिकेशन बनाएं

1. शुरुआती जानकारी

इस कोडलैब में, आपको फ़ोन, टैबलेट, और फ़ोल्ड किए जा सकने वाले डिवाइसों के हिसाब से ऐप्लिकेशन बनाने का तरीका पता चलेगा. साथ ही, यह भी पता चलेगा कि Jetpack Compose की मदद से, ये ऐप्लिकेशन बेहतर ढंग से पहुंच पाते हैं या नहीं. आपको मटीरियल 3 कॉम्पोनेंट और थीमिंग का इस्तेमाल करने के सबसे सही तरीकों के बारे में भी जानकारी मिलेगी.

आगे बढ़ने से पहले, यह समझना ज़रूरी है कि ज़रूरत के हिसाब से ढलने का हमारा मतलब क्या है.

अडैप्टेबिलिटी

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

ज़्यादा जानने के लिए, अडैप्टिव डिज़ाइन देखें.

इस कोडलैब में, आपको Jetpack Compose की सुविधा इस्तेमाल करने के बारे में जानकारी मिलेगी. साथ ही, यह बताया गया है कि इसे इस्तेमाल करते समय डिवाइस को ज़रूरत के हिसाब से कैसे इस्तेमाल करना है. आपने 'जवाब दें' नाम का एक ऐप्लिकेशन बनाया है. यह आपको बताता है कि हर तरह की स्क्रीन के हिसाब से, ज़रूरत के हिसाब से बदलाव करने की सुविधा कैसे लागू की जाए. साथ ही, इससे यह भी पता चलता है कि उपयोगकर्ताओं को बेहतर अनुभव देने के लिए, 'ज़रूरत के हिसाब से ढल जाने की सुविधा' और 'पहुंच' कैसे एक साथ काम करती हैं.

आप इन चीज़ों के बारे में जानेंगे

  • Jetpack Compose के ज़रिए सभी विंडो को टारगेट करने के लिए अपने ऐप्लिकेशन को कैसे डिज़ाइन करें.
  • फ़ोल्ड किए जा सकने वाले अलग-अलग डिवाइसों के लिए अपने ऐप्लिकेशन को टारगेट करने का तरीका.
  • सुलभता और आसानी से पहुंचने के लिए, अलग-अलग तरह के नेविगेशन का इस्तेमाल करने का तरीका.
  • हर साइज़ की विंडो के लिए बेहतरीन अनुभव देने के लिए, Material 3 कॉम्पोनेंट का इस्तेमाल करने का तरीका.

आपको इनकी ज़रूरत होगी

इस कोडलैब के लिए, साइज़ बदलने वाला एम्युलेटर का इस्तेमाल किया जाएगा. इससे आपको अलग-अलग तरह के डिवाइसों और विंडो साइज़ के बीच स्विच करने में मदद मिलेगी.

इस एम्युलेटर का साइज़ बदलने की सुविधा, जिसमें फ़ोन, अनफ़ोल्डेड, टैबलेट, और डेस्कटॉप के लिए विकल्प मौजूद हैं.

अगर आपको Compose के बारे में नहीं पता है, तो इस कोडलैब को पूरा करने से पहले, Jetpack Compose के बारे में बुनियादी बातें बताने वाला कोडलैब इस्तेमाल करें.

आपको क्या बनाना होगा

  • एक इंटरैक्टिव ईमेल क्लाइंट ऐप्लिकेशन, जिसमें ज़रूरत के हिसाब से डिज़ाइन करने, अलग-अलग मटीरियल नेविगेशन, और स्क्रीन स्पेस के इस्तेमाल के सबसे सही तरीकों का इस्तेमाल किया गया है.

एक से ज़्यादा डिवाइस के लिए सहायता शोकेस, जिसे आप इस कोडलैब में हासिल कर सकते हैं

2. सेट अप करने का तरीका

इस कोडलैब का कोड पाने के लिए, कमांड लाइन से GitHub रिपॉज़िटरी का क्लोन बनाएं:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

इसके अलावा, डेटा स्टोर करने की जगह को ZIP फ़ाइल के तौर पर डाउनलोड किया जा सकता है:

हमारा सुझाव है कि आप मुख्य ब्रांच में कोड से शुरू करें और अपनी रफ़्तार से कोडलैब का सिलसिलेवार तरीके से पालन करें.

प्रोजेक्ट को Android Studio में खोलें

  1. Android Studio में आपका स्वागत है विंडो में, c01826594f360d94.pngमौजूदा प्रोजेक्ट खोलें को चुनें.
  2. फ़ोल्डर <Download Location>/AdaptiveUiCodelab चुनें. पक्का करें कि आपने AdaptiveUiCodelab डायरेक्ट्री में build.gradle को चुना हो.
  3. Android Studio से प्रोजेक्ट इंपोर्ट हो जाने के बाद, जांच लें कि आपके पास main ब्रांच को चलाने का विकल्प है या नहीं.

स्टार्ट कोड के बारे में जानना

मुख्य शाखा कोड में ui पैकेज होता है. उस पैकेज में मौजूद इन फ़ाइलों के साथ काम किया जा सकता है:

  • MainActivity.kt - एंट्री पॉइंट से जुड़ी गतिविधि जहां से आपने अपना ऐप्लिकेशन शुरू किया.
  • ReplyApp.kt - इसमें मुख्य स्क्रीन के यूज़र इंटरफ़ेस (यूआई) कंपोज़ेबल शामिल हैं.
  • ReplyHomeViewModel.kt - यह ऐप्लिकेशन के कॉन्टेंट का डेटा और यूज़र इंटरफ़ेस (यूआई) की स्थिति दिखाता है.
  • ReplyListContent.kt - इसमें सूचियां और ज़्यादा जानकारी वाली स्क्रीन उपलब्ध कराने के लिए कंपोज़ेबल.

आपका फ़ोकस पहले MainActivity.kt पर होगा.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )
        }
    }
}

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

फ़ोन की शुरुआती स्क्रीन

टैबलेट पर शुरुआती स्ट्रेच किया गया व्यू

स्क्रीन की जगह का फ़ायदा लेने, उपयोगिता बढ़ाने, और उपयोगकर्ता अनुभव को बेहतर बनाने के लिए, ऐप्लिकेशन को अपडेट किया जाएगा.

3. डिवाइस के हिसाब से ऐप्लिकेशन बनाएं

इस सेक्शन में बताया गया है कि ऐप्लिकेशन को ज़रूरत के हिसाब से बनाने का क्या मतलब है और Material 3 किस तरह के कॉम्पोनेंट की मदद से इस प्रक्रिया को आसान बना सकती है. इसमें यह भी बताया गया है कि आपको किन स्क्रीन और स्थितियों को टारगेट करना है. इनमें फ़ोन, टैबलेट, बड़े टैबलेट, और फ़ोल्ड किए जा सकने वाले डिवाइस शामिल हैं.

विंडो साइज़, फ़ोल्ड पॉस्चर, और नेविगेशन के अलग-अलग विकल्पों के बारे में बुनियादी जानकारी पाएं. इसके बाद, अपने ऐप्लिकेशन में इन एपीआई का इस्तेमाल करके, बेहतर तरीके से काम किया जा सकता है.

विंडो के साइज़

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

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

छोटी, मीडियम, और बढ़ाई गई चौड़ाई के लिए WindowwidthSizeClass.

छोटी, मीडियम, और बढ़ाई गई ऊंचाई के लिए WindowHightSizeClass.

चौड़ाई और ऊंचाई, दोनों को अलग-अलग कैटगरी में रखा जाता है. इसलिए, किसी भी समय आपके ऐप्लिकेशन में विंडो साइज़ की दो क्लास होती हैं—एक चौड़ाई के लिए और दूसरी ऊंचाई के लिए. वर्टिकल स्क्रोलिंग की हर जगह उपलब्ध होने की वजह से, उपलब्ध चौड़ाई आम तौर पर उपलब्ध ऊंचाई की तुलना में ज़्यादा ज़रूरी होती है. इसलिए, इस मामले के लिए आप चौड़ाई के साइज़ की क्लास का भी इस्तेमाल करेंगे.

फ़ोल्ड स्टेट

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

फ़ोल्ड किए जा सकने वाले पॉस्चर, फ़्लैट, और आधा खुला हुआ

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

फ़ोल्ड पॉस्चर और हिंज के बारे में और पढ़ें.

फ़ोल्ड किए जा सकने वाले डिवाइसों के साथ काम करने वाले अडैप्टिव लेआउट इस्तेमाल करते समय, इन सभी बातों का ध्यान रखना चाहिए.

ज़रूरत के हिसाब से जानकारी पाना

Material3 adaptive लाइब्रेरी की मदद से, उस विंडो की जानकारी को आसानी से ऐक्सेस किया जा सकता है जिसमें आपका ऐप्लिकेशन चल रहा है.

  1. वर्शन कैटलॉग फ़ाइल में इस आर्टफ़ैक्ट और इसके वर्शन की एंट्री जोड़ें:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. ऐप्लिकेशन मॉड्यूल की बिल्ड फ़ाइल में, नई लाइब्रेरी डिपेंडेंसी जोड़ें और फिर Gradle सिंक करें:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

अब किसी भी कंपोज़ेबल स्कोप में currentWindowAdaptiveInfo() का इस्तेमाल करके, एक WindowAdaptiveInfo ऑब्जेक्ट हासिल किया जा सकता है. इस ऑब्जेक्ट में विंडो के साइज़ की मौजूदा क्लास जैसी जानकारी शामिल होती है. साथ ही, यह भी पता लगाया जा सकता है कि डिवाइस फ़ोल्ड किए जा सकने वाले पोज़िशन में है या नहीं, जैसे कि टेबलटॉप पॉस्चर.

MainActivity में जाकर, इसे अभी आज़माया जा सकता है.

  1. ReplyTheme ब्लॉक के अंदर onCreate() में, विंडो के हिसाब से जानकारी पाएं और Text कंपोज़ेबल में साइज़ क्लास दिखाएं (इसे ReplyApp() एलिमेंट के बाद जोड़ा जा सकता है):

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )

            val adaptiveInfo = currentWindowAdaptiveInfo()
            val sizeClassText =
                "${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
                "${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
            Text(
                text = sizeClassText,
                color = Color.Magenta,
                modifier = Modifier.padding(20.dp)
            )
        }
    }
}

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

4. डाइनैमिक नेविगेशन

अब आपको ऐप्लिकेशन के नेविगेशन करने के तरीके में बदलाव करने होंगे, ताकि डिवाइस की स्थिति और साइज़ में बदलाव हो. इससे आपके डिवाइसों को बेहतर तरीके से ऐक्सेस किया जा सकेगा.

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

  • डिवाइस को पकड़ते समय किन जगहों पर पहुंचना आसान होता है?
  • किन हिस्सों पर सिर्फ़ उंगलियों को आगे बढ़ाने पर पहुंचा जा सकता है, जिसमें दिक्कत हो सकती है?
  • किन इलाकों तक पहुंचना मुश्किल है या उपयोगकर्ता के पास डिवाइस कहां से दूर है?

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

बॉटम नेविगेशन

बॉटम नेविगेशन की सुविधा, कॉम्पैक्ट साइज़ वाले डिवाइसों पर अच्छी तरह काम करती है. ऐसा इसलिए, क्योंकि हम अपने-आप डिवाइस को ऐसी जगह पकड़ते हैं जहां हमारा अंगूठा नीचे वाले नेविगेशन के सभी टच पॉइंट तक आसानी से पहुंच सके. इसका इस्तेमाल तब करें, जब आपके डिवाइस का साइज़ छोटा हो या फ़ोल्ड किया जा सकने वाला छोटा डिवाइस हो.

सबसे नीचे मौजूद नेविगेशन बार, जिसमें आइटम मौजूद हैं

मध्यम चौड़ाई वाली विंडो के लिए नेविगेशन रेल आसानी से पहुंच लायक है, क्योंकि हमारा अंगूठा डिवाइस के किनारों पर ही पड़ता है. ज़्यादा जानकारी दिखाने के लिए, एक नेविगेशन रेल को नेविगेशन पैनल के साथ जोड़ा जा सकता है.

आइटम के साथ नेविगेशन रेल

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

मोडल नेविगेशन पैनल

छोटे से लेकर मीडियम साइज़ के फ़ोन और टैबलेट के लिए, मोडल नेविगेशन पैनल का इस्तेमाल किया जा सकता है. ऐसा इसलिए, क्योंकि कॉन्टेंट पर ओवरले के रूप में, इसे बड़ा किया जा सकता है या छिपाया जा सकता है. इसे कभी-कभी नेविगेशन रेल के साथ जोड़ा जा सकता है.

आइटम के साथ मोडल नेविगेशन पैनल

स्थायी नेविगेशन पैनल

बड़े टैबलेट, Chromebook, और डेस्कटॉप पर स्थायी नेविगेशन पैनल का इस्तेमाल करके, स्थायी नेविगेशन पैनल का इस्तेमाल किया जा सकता है.

आइटम के साथ स्थायी नेविगेशन पैनल

डाइनैमिक नेविगेशन को लागू करना

अब डिवाइस की स्थिति और साइज़ में बदलाव होने पर, नेविगेशन के अलग-अलग तरीके चुने जा सकते हैं.

फ़िलहाल, ऐप्लिकेशन हमेशा स्क्रीन पर मौजूद कॉन्टेंट के नीचे NavigationBar दिखाता है. भले ही, डिवाइस की स्थिति कुछ भी हो. इसके बजाय, विंडो के मौजूदा साइज़ क्लास जैसी जानकारी के आधार पर, नेविगेशन के अलग-अलग कॉम्पोनेंट के बीच अपने-आप स्विच करने के लिए, मटीरियल NavigationSuiteScaffold कॉम्पोनेंट का इस्तेमाल किया जा सकता है.

  1. वर्शन कैटलॉग और ऐप्लिकेशन की बिल्ड स्क्रिप्ट को अपडेट करके, यह कॉम्पोनेंट पाने के लिए Gradle डिपेंडेंसी जोड़ें. इसके बाद, Gradle सिंक करें:

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0-beta01"

[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.navigation.suite)
}
  1. ReplyApp.kt में ReplyNavigationWrapper() कंपोज़ेबल फ़ंक्शन ढूंढें और Column और उसके कॉन्टेंट को NavigationSuiteScaffold से बदलें:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            ReplyDestination.entries.forEach {
                item(
                    selected = it == selectedDestination,
                    onClick = { /*TODO update selection*/ },
                    icon = {
                        Icon(
                            imageVector = it.icon,
                            contentDescription = stringResource(it.labelRes)
                        )
                    },
                    label = {
                        Text(text = stringResource(it.labelRes))
                    },
                )
            }
        }
    ) {
        content()
    }
}

navigationSuiteItems आर्ग्युमेंट ऐसा ब्लॉक है जिससे आपको item() फ़ंक्शन का इस्तेमाल करके आइटम जोड़ने की सुविधा मिलती है. यह बिलकुल LazyColumn में आइटम जोड़ने जैसा ही है. पीछे वाले लैम्डा के अंदर, यह कोड ReplyNavigationWrapperUI() को तर्क के रूप में पास किए गए content() को कॉल करता है.

एम्युलेटर पर ऐप्लिकेशन चलाएं और फ़ोन, फ़ोल्ड किए जा सकने वाले डिवाइस, और टैबलेट के बीच साइज़ बदलकर देखें. इसके बाद, आपका नेविगेशन बार दिखेगा, जो नेविगेशन रेल में बदल जाएगा और पीछे हट जाएगा.

बहुत चौड़ी विंडो, जैसे कि लैंडस्केप मोड में टैबलेट पर, हो सकता है कि आप हमेशा के लिए नेविगेशन पैनल दिखाना चाहें. NavigationSuiteScaffold स्थायी ड्रॉर दिखाने का समर्थन करता है, हालांकि यह मौजूदा WindowWidthSizeClass मानों में से किसी में भी नहीं दिखाया जाता. हालांकि, छोटा सा बदलाव करके ऐसा किया जा सकता है.

  1. NavigationSuiteScaffold पर कॉल करने से ठीक पहले यह कोड जोड़ें:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    val windowSize = with(LocalDensity.current) {
        currentWindowSize().toSize().toDpSize()
    }
    val layoutType = if (windowSize.width >= 1200.dp) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
            currentWindowAdaptiveInfo()
        )
    }

    NavigationSuiteScaffold(
        layoutType = layoutType,
        ...
    ) {
        content()
    }
}

यह कोड सबसे पहले विंडो का साइज़ लेता है और उसे currentWindowSize() और LocalDensity.current का इस्तेमाल करके डीपी यूनिट में बदलता है. इसके बाद, यह कोड नेविगेशन के यूज़र इंटरफ़ेस (यूआई) का लेआउट टाइप तय करने के लिए, विंडो की चौड़ाई की तुलना करता है. अगर विंडो की चौड़ाई कम से कम 1200.dp है, तो NavigationSuiteType.NavigationDrawer का इस्तेमाल किया जाता है. ऐसा न करने पर, कैलकुलेट करने का डिफ़ॉल्ट तरीका लागू हो जाता है.

साइज़ बदलने वाले एम्युलेटर पर ऐप्लिकेशन को दोबारा चलाने और अलग-अलग तरह के डिवाइस आज़माने पर ध्यान दें कि जब भी स्क्रीन के कॉन्फ़िगरेशन में बदलाव हो या किसी फ़ोल्डिंग डिवाइस को अनफ़ोल्ड किया जाए, तब नेविगेशन उस साइज़ के हिसाब से सही साइज़ में बदल जाए.

डिवाइसों के अलग-अलग साइज़ के हिसाब से, ज़रूरत के मुताबिक किए जा सकने वाले बदलाव दिखाए जा रहे हैं.

बधाई हो, आपने अलग-अलग साइज़ की विंडो और स्टेट के साथ काम करने के लिए, नेविगेशन के अलग-अलग टाइप के बारे में जाना सीख लिया है!

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

5. स्क्रीन स्पेस का इस्तेमाल

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

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

  1. यह कॉम्पोनेंट पाने के लिए, इन डिपेंडेंसी जोड़ें और Gradle सिंक करें:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.layout)
    implementation(libs.androidx.material3.adaptive.navigation)
}
  1. ReplyApp.kt में, ReplyAppContent() कंपोज़ेबल फ़ंक्शन ढूंढें. फ़िलहाल, यह सिर्फ़ ReplyListPane() को कॉल करके सूची पैनल दिखाता है. नीचे दिया गया कोड डालकर, इस तरीके को ListDetailPaneScaffold से बदलें. यह एपीआई, एक्सपेरिमेंट के तौर पर शुरू किया गया है. इसलिए, आपको ReplyAppContent() फ़ंक्शन में @OptIn एनोटेशन भी जोड़ना होगा:

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            ReplyListPane(replyHomeUIState, onEmailClick)
        },
        detailPane = {
            ReplyDetailPane(replyHomeUIState.emails.first())
        }
    )
}

यह कोड सबसे पहले rememberListDetailPaneNavigator() का इस्तेमाल करके नेविगेटर बनाता है. नेविगेटर कुछ कंट्रोल देता है कि कौनसा पैनल दिखाया जाए और उस पैनल में कौनसा कॉन्टेंट दिखाया जाए, जिसे बाद में दिखाया जाएगा.

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

बाकी के ज़रूरी पैरामीटर, पैनल के लिए कंपोज़ेबल लैम्डा हैं. ReplyListPane() और ReplyDetailPane() (ReplyListContent.kt में मौजूद) का इस्तेमाल, क्रमश: सूची और जानकारी वाले पैनल की भूमिकाओं को भरने के लिए किया जाता है. ReplyDetailPane() को ईमेल तर्क की उम्मीद है, इसलिए अभी के लिए यह कोड ReplyHomeUIState में मौजूद ईमेल की सूची में से पहले ईमेल का इस्तेमाल करता है.

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

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

  1. ReplyHomeViewModel.kt खोलें और ReplyHomeUIState डेटा क्लास ढूंढें. चुने गए ईमेल के लिए null की डिफ़ॉल्ट वैल्यू के साथ एक प्रॉपर्टी जोड़ें:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. उसी फ़ाइल में, ReplyHomeViewModel का setSelectedEmail() फ़ंक्शन है. इसे तब कॉल किया जाता है, जब उपयोगकर्ता किसी सूची में मौजूद आइटम पर टैप करता है. यूज़र इंटरफ़ेस (यूआई) की स्थिति को कॉपी करने और चुने गए ईमेल को रिकॉर्ड करने के लिए, इस फ़ंक्शन में बदलाव करें:

ReplyHomeViewModel.kt

fun setSelectedEmail(email: Email) {
    _uiState.update {
        it.copy(selectedEmail = email)
    }
}

ध्यान देने वाली बात यह है कि उपयोगकर्ता के किसी आइटम पर टैप करने से पहले क्या होता है और चुना गया ईमेल null है. ब्यौरे वाले पैनल में क्या दिखाया जाना चाहिए? इस मामले को मैनेज करने के कई तरीके हैं. जैसे, डिफ़ॉल्ट रूप से, सूची का पहला आइटम दिखाना.

  1. उसी फ़ाइल में, observeEmails() फ़ंक्शन में बदलाव करें. अगर ईमेल की सूची लोड हो जाती है और पिछली यूज़र इंटरफ़ेस (यूआई) स्थिति में कोई ईमेल पता नहीं चुना गया था, तो ईमेल की सूची को पहले आइटम पर सेट करें:

ReplyHomeViewModel.kt

private fun observeEmails() {
    viewModelScope.launch {
        emailsRepository.getAllEmails()
            .catch { ex ->
                _uiState.value = ReplyHomeUIState(error = ex.message)
            }
            .collect { emails ->
                val currentSelection = _uiState.value.selectedEmail
                _uiState.value = ReplyHomeUIState(
                    emails = emails,
                    selectedEmail = currentSelection ?: emails.first()
                )
            }
    }
}
  1. ReplyApp.kt पर वापस जाएं और ज़्यादा जानकारी वाले पैनल में मौजूद कॉन्टेंट को अपने-आप भरने के लिए, अगर चुना गया ईमेल पता उपलब्ध है, तो उसका इस्तेमाल करें:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    detailPane = {
        if (replyHomeUIState.selectedEmail != null) {
            ReplyDetailPane(replyHomeUIState.selectedEmail)
        }
    }
)

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

दोनों पैनल के दिखने पर यह बढ़िया काम करता है. हालांकि, जब विंडो में सिर्फ़ एक पैनल दिखाने के लिए जगह हो, तो ऐसा लगता है कि आइटम पर टैप करने से कुछ नहीं होगा. एम्युलेटर व्यू को फ़ोन या फ़ोल्ड किए जा सकने वाले डिवाइस पर पोर्ट्रेट मोड में आज़माकर देखें. ध्यान दें कि किसी आइटम पर टैप करने के बाद भी, सूची वाला पैनल ही दिखता है. ऐसा इसलिए, क्योंकि भले ही चुना गया ईमेल अपडेट किया गया हो, लेकिन ListDetailPaneScaffold इन कॉन्फ़िगरेशन में सूची पैनल पर फ़ोकस बनाए रखता है.

  1. इसे ठीक करने के लिए, ReplyListPane को पास किए गए Lambda फ़ंक्शन के तौर पर यह कोड डालें:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    listPane = {
        ReplyListPane(
            replyHomeUIState = replyHomeUIState,
            onEmailClick = { email ->
                onEmailClick(email)
                navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
            }
        )
    },
    // ...
)

किसी आइटम पर क्लिक करने पर अतिरिक्त व्यवहार जोड़ने के लिए, यह Lambda फ़ंक्शन पहले बनाए गए नेविगेटर का इस्तेमाल करता है. यह इस फ़ंक्शन को पास किए गए मूल Lambda फ़ंक्शन को कॉल करेगा. इसके बाद, navigator.navigateTo() को कॉल करेगा, ताकि यह तय किया जा सके कि कौनसा पैनल दिखाया जाना चाहिए. मचान के हर पैनल के साथ एक भूमिका जुड़ी होती है और डिटेल पैनल के लिए यह ListDetailPaneScaffoldRole.Detail है. छोटी विंडो पर, ऐसा दिखेगा कि ऐप्लिकेशन ने आगे की ओर नेविगेट किया है.

ऐप्लिकेशन को यह भी मैनेज करना होगा कि जब उपयोगकर्ता ज़्यादा जानकारी वाले पैनल से 'वापस जाएं' बटन दबाता है, तो क्या होता है. यह तरीका इस आधार पर अलग-अलग होगा कि एक पैनल दिख रहा है या दो पैनल दिख रहे हैं.

  1. नीचे दिया गया कोड जोड़कर, नेविगेशन बार में इसकी सुविधा दें.

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                ReplyListPane(
                    replyHomeUIState = replyHomeUIState,
                    onEmailClick = { email ->
                        onEmailClick(email)
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
                    }
                )
            }
        },
        detailPane = {
            AnimatedPane {
                if (replyHomeUIState.selectedEmail != null) {
                    ReplyDetailPane(replyHomeUIState.selectedEmail)
                }
            }
        }
    )
}

नेविगेटर को ListDetailPaneScaffold की पूरी स्थिति पता होती है. साथ ही, यह भी पता होता है कि वापस जाना संभव है या नहीं और इन सभी स्थितियों में क्या करना है. यह कोड एक BackHandler बनाता है जो नेविगेटर के वापस नेविगेट करने पर चालू होता है. साथ ही, यह Lambda फ़ंक्शन में, navigateBack() को कॉल करता है. साथ ही, पैनल के बीच ट्रांज़िशन को ज़्यादा आसान बनाने के लिए, हर पैनल को AnimatedPane() कंपोज़ेबल में रैप किया जाता है.

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

डिवाइसों के अलग-अलग साइज़ के हिसाब से, ज़रूरत के मुताबिक किए जा सकने वाले बदलाव दिखाए जा रहे हैं.

बधाई हो, आपने अपने ऐप्लिकेशन को सभी तरह के डिवाइस की स्थितियों और साइज़ के मुताबिक बना लिया है. फ़ोल्ड किए जा सकने वाले डिवाइस, टैबलेट या अन्य मोबाइल डिवाइसों पर इस ऐप्लिकेशन को चलाकर देखें.

6. बधाई

बधाई हो! आपने इस कोडलैब को पूरा कर लिया है और Jetpack Compose की मदद से ऐप्लिकेशन के हिसाब से ऐप्लिकेशन बनाने का तरीका सीख लिया है.

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

आगे क्या करना है?

लिखें पाथ पर अन्य कोडलैब देखें.

सैंपल ऐप्लिकेशन

  • कंपोज़ सैंपल, कई ऐप्लिकेशन का कलेक्शन होते हैं. इनमें कोडलैब में बताए गए सबसे सही तरीके शामिल होते हैं.

रेफ़रंस के लिए दस्तावेज़