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

1. परिचय

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

इस बारे में ज़्यादा जानने से पहले, यह समझना ज़रूरी है कि 'अडैप्टैबिलिटी' का क्या मतलब है.

बदलावों के हिसाब से ढल जाना

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

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

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

आपको क्या सीखने को मिलेगा

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

आपको किन चीज़ों की ज़रूरत होगी

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

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

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

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

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

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

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 शाखा को चलाया जा सकता है या नहीं.

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

main ब्रैंच कोड में ui पैकेज शामिल है. आपको उस पैकेज में मौजूद इन फ़ाइलों पर काम करना होगा:

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

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

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

टैबलेट पर शुरू में दिखने वाला स्ट्रेच किया गया व्यू

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

3. ऐप्लिकेशन को अलग-अलग डिवाइसों के हिसाब से बनाना

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

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

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

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

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

कॉम्पैक्ट, मीडियम, और बड़ा किए गए विंडो साइज़ के लिए WindowWidthSizeClass.

कॉम्पैक्ट, मीडियम, और बड़ी ऊंचाई के लिए WindowHeightSizeClass.

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

फ़ोल्ड की स्थितियां

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

फ़ोल्ड किए जा सकने वाले पोज़िशन, फ़्लैट, और आधी स्क्रीन में खुला

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

फ़ोल्ड पोज़िशन और हिंज के बारे में ज़्यादा जानें.

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

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

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

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

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0"

[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(
                    WindowInsets.safeDrawing.asPaddingValues()
                )
            )
        }
    }
}

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

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

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

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

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

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

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

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

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

आइटम वाला बॉटम नेविगेशन बार

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

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

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

मोडल नेविगेशन ड्रॉअर

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

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

नेविगेशन पैनल हमेशा दिखना

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

आइटम के साथ हमेशा दिखने वाला नेविगेशन पैनल

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

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

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

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

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0"

[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. स्क्रीन स्पेस का इस्तेमाल

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

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

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

gradle/libs.versions.toml

[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 में पास किए गए लैंब्डा के तौर पर यह कोड डालें:

ReplyApp.kt

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

यह लैम्ब्डा, पहले बनाए गए नेविगेटर का इस्तेमाल करके, किसी आइटम पर क्लिक करने पर अतिरिक्त व्यवहार जोड़ता है. यह इस फ़ंक्शन में पास किए गए ओरिजनल लैम्ब्डा को कॉल करेगा. इसके बाद, यह 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 बनाता है, जो तब चालू होता है, जब नेविगेटर वापस नेविगेट कर सकता है. साथ ही, यह लैम्ब्डा कॉल में navigateBack() को कॉल करता है. साथ ही, पैनल के बीच ट्रांज़िशन को ज़्यादा आसान बनाने के लिए, हर पैनल को AnimatedPane() कॉम्पोज़ेबल में लपेटा जाता है.

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

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

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

6. बधाई हो

बधाई हो! आपने यह कोडलैब पूरा कर लिया है. साथ ही, Jetpack Compose की मदद से ऐप्लिकेशन को अलग-अलग डिवाइसों के हिसाब से बनाने का तरीका भी जाना है.

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

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

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

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

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

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