लिखने की सुविधा के साथ नेविगेशन

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

खास तौर पर Compose के लिए बनाई गई, नेविगेशन लाइब्रेरी के नए वर्शन के बारे में जानने के लिए, Navigation 3 का दस्तावेज़ देखें.

सेटअप

Compose का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन मॉड्यूल की build.gradle फ़ाइल में यह डिपेंडेंसी इस्तेमाल करें:

Groovy

dependencies {
    def nav_version = "2.9.4"

    implementation "androidx.navigation:navigation-compose:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.9.4"

    implementation("androidx.navigation:navigation-compose:$nav_version")
}

शुरू करें

किसी ऐप्लिकेशन में नेविगेशन लागू करते समय, नेविगेशन होस्ट, ग्राफ़, और कंट्रोलर लागू करें. ज़्यादा जानकारी के लिए, नेविगेशन की खास जानकारी देखें.

Compose में NavController बनाने के तरीके के बारे में जानकारी के लिए, नेविगेशन कंट्रोलर बनाएं लेख का Compose सेक्शन देखें.

NavHost बनाना

Compose में NavHost बनाने के तरीके के बारे में जानने के लिए, अपना नेविगेशन ग्राफ़ डिज़ाइन करना लेख का Compose सेक्शन देखें.

किसी कंपोज़ेबल पर जाने के बारे में जानकारी पाने के लिए, आर्किटेक्चर के दस्तावेज़ में किसी डेस्टिनेशन पर नेविगेट करना लेख पढ़ें.

कंपोज़ेबल डेस्टिनेशन के बीच आर्ग्युमेंट पास करने के बारे में जानकारी पाने के लिए, अपना नेविगेशन ग्राफ़ डिज़ाइन करना के Compose सेक्शन पर जाएं.

नेविगेट करते समय जटिल डेटा पाना

हमारा सुझाव है कि नेविगेट करते समय, जटिल डेटा ऑब्जेक्ट को पास न करें. इसके बजाय, नेविगेशन की कार्रवाइयां करते समय, ज़रूरी जानकारी को आर्ग्युमेंट के तौर पर पास करें. जैसे, यूनीक आइडेंटिफ़ायर या आईडी का कोई अन्य फ़ॉर्म:

// Pass only the user ID when navigating to a new destination as argument
navController.navigate(Profile(id = "user1234"))

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

class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {

    private val profile = savedStateHandle.toRoute<Profile>()

    // Fetch the relevant user information from the data layer,
    // ie. userInfoRepository, based on the passed userId argument
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(profile.id)

// …

}

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

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

Navigation Compose, डीप लिंक के साथ काम करता है. इन्हें composable() फ़ंक्शन के हिस्से के तौर पर भी तय किया जा सकता है. इसका deepLinks पैरामीटर, NavDeepLink ऑब्जेक्ट की सूची स्वीकार करता है. इन्हें navDeepLink() तरीके का इस्तेमाल करके तुरंत बनाया जा सकता है:

@Serializable data class Profile(val id: String)
val uri = "https://www.example.com"

composable<Profile>(
  deepLinks = listOf(
    navDeepLink<Profile>(basePath = "$uri/profile")
  )
) { backStackEntry ->
  ProfileScreen(id = backStackEntry.toRoute<Profile>().id)
}

इन डीप लिंक की मदद से, किसी कंपोज़ेबल के साथ कोई यूआरएल, कार्रवाई या माइम टाइप जोड़ा जा सकता है. डिफ़ॉल्ट रूप से, इन डीप लिंक को बाहरी ऐप्लिकेशन के साथ शेयर नहीं किया जाता. इन डीप लिंक को बाहरी तौर पर उपलब्ध कराने के लिए, आपको अपने ऐप्लिकेशन की <intent-filter> फ़ाइल में सही <intent-filter> एलिमेंट जोड़ने होंगे.manifest.xml ऊपर दिए गए उदाहरण में डीप लिंक चालू करने के लिए, आपको मेनिफ़ेस्ट के <activity> एलिमेंट में यह जोड़ना होगा:

<activity …>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

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

इन्हीं डीप लिंक का इस्तेमाल करके, कंपोज़ेबल से सही डीप लिंक के साथ PendingIntent बनाया जा सकता है:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/profile/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

इसके बाद, इस deepLinkPendingIntent का इस्तेमाल किसी भी अन्य PendingIntent की तरह किया जा सकता है. इससे डीप लिंक डेस्टिनेशन पर आपका ऐप्लिकेशन खुल जाएगा.

नेस्ट किया गया नेविगेशन

नेस्ट किए गए नेविगेशन ग्राफ़ बनाने के बारे में जानने के लिए, नेस्ट किए गए ग्राफ़ देखें.

अडैप्टिव बॉटम नेविगेशन बार और नेविगेशन रेल बनाना

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

ज़्यादा जानकारी के लिए, अनुकूलित नेविगेशन बनाना लेख पढ़ें.

इंटरऑपरेबिलिटी (दूसरे डिवाइस के साथ काम करना)

अगर आपको Compose के साथ Navigation कॉम्पोनेंट का इस्तेमाल करना है, तो आपके पास दो विकल्प हैं:

  • फ़्रैगमेंट के लिए, Navigation कॉम्पोनेंट की मदद से नेविगेशन ग्राफ़ तय करें.
  • Compose destinations का इस्तेमाल करके, Compose में NavHost के साथ नेविगेशन ग्राफ़ तय करें. ऐसा सिर्फ़ तब किया जा सकता है, जब नेविगेशन ग्राफ़ में मौजूद सभी स्क्रीन कंपोज़ेबल हों.

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

Compose कोड में डेस्टिनेशन बदलने के लिए, ऐसे इवेंट दिखाए जाते हैं जिन्हें हैरारकी में मौजूद किसी भी कंपोज़ेबल को पास किया जा सकता है और ट्रिगर किया जा सकता है:

@Composable
fun MyScreen(onNavigate: (Int) -> Unit) {
    Button(onClick = { onNavigate(R.id.nav_profile) } { /* ... */ }
}

अपने फ़्रैगमेंट में, Compose और फ़्रैगमेंट पर आधारित नेविगेशन कॉम्पोनेंट के बीच ब्रिज बनाएं. इसके लिए, NavController ढूंढें और डेस्टिनेशन पर जाएं:

override fun onCreateView( /* ... */ ) {
    setContent {
        MyScreen(onNavigate = { dest -> findNavController().navigate(dest) })
    }
}

इसके अलावा, NavController को अपनी कंपोज़ हैरारकी में पास किया जा सकता है. हालांकि, सामान्य फ़ंक्शन को दिखाने से, उन्हें कई बार इस्तेमाल किया जा सकता है और उनकी जांच की जा सकती है.

टेस्ट करना

नेविगेशन कोड को कंपोज़ेबल डेस्टिनेशन से अलग करें, ताकि हर कंपोज़ेबल की अलग से जांच की जा सके. इसके लिए, NavHost कंपोज़ेबल से अलग करें.

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

composable लैम्डा की मदद से, इनडायरेक्शन का लेवल तय किया जाता है. इससे आपको अपने नेविगेशन कोड को कंपोज़ेबल से अलग करने में मदद मिलती है. यह सुविधा दो तरह से काम करती है:

  • अपने कंपोज़ेबल में सिर्फ़ पार्स किए गए आर्ग्युमेंट पास करें
  • नेविगेट करने के लिए, कंपोज़ेबल को ट्रिगर करने वाले लैम्ब्डा पास करें. इसके बजाय, NavController को ट्रिगर करने वाले लैम्ब्डा पास करें.

उदाहरण के लिए, ProfileScreen कंपोज़ेबल, userId को इनपुट के तौर पर लेता है. साथ ही, उपयोगकर्ताओं को किसी दोस्त के प्रोफ़ाइल पेज पर जाने की अनुमति देता है. इसका सिग्नेचर यह हो सकता है:

@Composable
fun ProfileScreen(
    userId: String,
    navigateToFriendProfile: (friendUserId: String) -> Unit
) {
 
}

इस तरह, ProfileScreen कंपोज़ेबल, नेविगेशन से अलग काम करता है. इससे इसे अलग से टेस्ट किया जा सकता है. composable लैंबडा, Navigation API और आपके कंपोज़ेबल के बीच के अंतर को कम करने के लिए ज़रूरी लॉजिक को शामिल करेगा:

@Serializable data class Profile(id: String)

composable<Profile> { backStackEntry ->
    val profile = backStackEntry.toRoute<Profile>()
    ProfileScreen(userId = profile.id) { friendUserId ->
        navController.navigate(route = Profile(id = friendUserId))
    }
}

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

NavHost की जांच की जा रही है

अपने NavHost की टेस्टिंग शुरू करने के लिए , नेविगेशन की टेस्टिंग से जुड़ी यह डिपेंडेंसी जोड़ें:

dependencies {
// ...
  androidTestImplementation "androidx.navigation:navigation-testing:$navigationVersion"
  // ...
}

अपने ऐप्लिकेशन के NavHost को ऐसे कंपोज़ेबल में रैप करें जो NavHostController को पैरामीटर के तौर पर स्वीकार करता हो.

@Composable
fun AppNavHost(navController: NavHostController){
  NavHost(navController = navController){ ... }
}

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

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()
    lateinit var navController: TestNavHostController

    @Before
    fun setupAppNavHost() {
        composeTestRule.setContent {
            navController = TestNavHostController(LocalContext.current)
            navController.navigatorProvider.addNavigator(ComposeNavigator())
            AppNavHost(navController = navController)
        }
    }

    // Unit test
    @Test
    fun appNavHost_verifyStartDestination() {
        composeTestRule
            .onNodeWithContentDescription("Start Screen")
            .assertIsDisplayed()
    }
}

नेविगेशन से जुड़ी कार्रवाइयों की टेस्टिंग

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

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

navController का इस्तेमाल करके, अपने दावे की जांच भी की जा सकती है. इसके लिए, navController के currentBackStackEntry का इस्तेमाल करके, मौजूदा रूट की तुलना अनुमानित रूट से करें:

@Test
fun appNavHost_clickAllProfiles_navigateToProfiles() {
    composeTestRule.onNodeWithContentDescription("All Profiles")
        .performScrollTo()
        .performClick()

    assertTrue(navController.currentBackStackEntry?.destination?.hasRoute<Profile>() ?: false)
}

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

ज़्यादा जानें

Jetpack Navigation के बारे में ज़्यादा जानने के लिए, Navigation कॉम्पोनेंट का इस्तेमाल शुरू करना लेख पढ़ें या Jetpack Compose Navigation का कोडलैब पूरा करें.

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

मॉड्यूलर ऐप्लिकेशन में Compose नेविगेशन को ज़्यादा बेहतर तरीके से लागू करने के बारे में जानने के लिए, GitHub पर Now in Android ऐप्लिकेशन देखें. इसमें नेस्ट किए गए ग्राफ़ और बॉटम नेविगेशन बार इंटिग्रेशन जैसे कॉन्सेप्ट शामिल हैं.

सैंपल