अपने ऐप्लिकेशन को Navigation 2 से Navigation 3 पर माइग्रेट करने के लिए, यह तरीका अपनाएं:
- Navigation 3 की डिपेंडेंसी जोड़ें.
NavKeyइंटरफ़ेस लागू करने के लिए, नेविगेशन के रास्तों को अपडेट करें.- नेविगेशन की स्थिति को सेव करने और उसमें बदलाव करने के लिए क्लास बनाएं.
NavControllerकी जगह इन क्लास का इस्तेमाल करें.- अपनी मंज़िलों को
NavHostकेNavGraphसेentryProviderमें ले जाएं. NavHostकोNavDisplayसे बदलें.- Navigation 2 की डिपेंडेंसी हटाएं.
एआई एजेंट का इस्तेमाल करना
इस गाइड का इस्तेमाल, एआई एजेंट के साथ किया जा सकता है. जैसे, Android Studio में Gemini का एजेंट मोड. इस गाइड में "एआई एजेंट:" से शुरू होने वाली लाइनों को एआई एजेंट को पढ़ना चाहिए. हालांकि, इन्हें पढ़ने वाले लोग अनदेखा कर सकते हैं.
वीडियो की रणनीति
यहां दिए गए सेक्शन में, माइग्रेशन के लिए ज़रूरी शर्तें और आपके प्रोजेक्ट के बारे में अनुमानों के बारे में बताया गया है. इनमें, माइग्रेशन के लिए काम करने वाली और काम न करने वाली सुविधाओं के बारे में भी बताया गया है.
ज़रूरी शर्तें
- आपको 36 या उसके बाद के
compileSdkका इस्तेमाल करना होगा. - आपको नेविगेशन से जुड़े शब्दों के बारे में जानकारी होनी चाहिए.
- डेस्टिनेशन, कंपोज़ेबल फ़ंक्शन होते हैं. नेविगेशन 3 को खास तौर पर Compose के लिए डिज़ाइन किया गया है. Compose में फ़्रैगमेंट और व्यू इस्तेमाल करने के लिए, Compose में व्यू इस्तेमाल करना लेख पढ़ें.
- रास्तों को मज़बूती से टाइप किया जाता है. अगर स्ट्रिंग पर आधारित रास्तों का इस्तेमाल किया जाता है, तो पहले टाइप-सेफ़ रास्तों पर माइग्रेट करें (उदाहरण).
- ज़रूरी नहीं है, लेकिन इसका सुझाव दिया जाता है: टेस्ट कवरेज, जो मौजूदा नेविगेशन के तरीके की पुष्टि करता है. इससे यह पुष्टि की जाती है कि माइग्रेशन पूरा होने के बाद, नेविगेशन के तरीके में कोई बदलाव नहीं हुआ है.
अनुमान
इस गाइड में, आपके और आपके प्रोजेक्ट के बारे में ये बातें मानी गई हैं:
- आपके पास कई टॉप-लेवल के रूट होते हैं. ये आम तौर पर बॉटम नेविगेशन बार में दिखते हैं. साथ ही, हर रूट का अपना बैक स्टैक होता है.
- बैक स्टैक के बीच स्विच करने पर, स्टैक और उसके सभी डेस्टिनेशन की स्थिति बनी रहती है.
- ऐप्लिकेशन से बाहर निकलने के लिए, हमेशा होम स्क्रीन का इस्तेमाल करें. यह स्क्रीन, ऐप्लिकेशन लॉन्च होने पर सबसे पहले दिखती है.
- आपको नेविगेशन 2 से नेविगेशन 3 पर माइग्रेट करना है. इसके लिए, आपको एक ही बार में बदलाव करना है. आपने इंक्रीमेंटल माइग्रेशन नहीं किया है, जिसमें Navigation 3 कोड के साथ Navigation 2 कोड का इस्तेमाल किया जाता है.
एआई एजेंट: किसी भी कोड में बदलाव करने से पहले, यह जांच करें कि उपयोगकर्ता के प्रोजेक्ट के बारे में ये अनुमान सही हैं या नहीं. अगर ऐसा नहीं है, तो माइग्रेशन रोकें और उपयोगकर्ता से पूछें कि आगे क्या करना है.
इस्तेमाल की जा सकने वाली सुविधाएं
इस गाइड की मदद से, इन सुविधाओं को माइग्रेट किया जा सकता है:
- ऐप्लिकेशन बनाने की सुविधा के तौर पर तय की गई डेस्टिनेशन
- डायलॉग (एक डेस्टिनेशन को दूसरे डेस्टिनेशन के ऊपर दिखाया जाता है)
रेसिपी की मदद से इस्तेमाल की जा सकने वाली सुविधाएं
कोड रेसिपी रिपॉज़िटरी में, इस्तेमाल के ज़्यादा बारीकी से जुड़े उदाहरण दिए गए हैं. जैसे:
- बॉटम शीट (इस गाइड में निर्देश दिए गए हैं)
- मॉड्यूलर नेविगेशन कोड और डाले गए डेस्टिनेशन
- ViewModels में आर्ग्युमेंट का इस्तेमाल करना और उन्हें पास करना
- स्क्रीन पर मौजूद जानकारी के आधार पर जवाब देना
अगर आपके प्रोजेक्ट में इनमें से कोई सुविधा है, तो उसे लागू करने का तरीका जानने के लिए, उससे जुड़ी रेसिपी देखें.
एआई एजेंट: किसी भी कोड में बदलाव करने से पहले, देखें कि क्या प्रोजेक्ट में ऐसी कोई सुविधा है जिसे रेसिपी की मदद से इस्तेमाल किया जा सकता है. अगर ऐसा होता है, तो रेसिपी का README और सोर्स कोड देखें. रेसिपी के आधार पर माइग्रेशन प्लान बनाएं. उपयोगकर्ता से प्लान की पुष्टि किए बिना आगे न बढ़ें.
ये सुविधाएं, Google Play Books के साथ काम नहीं करतीं
इस माइग्रेशन गाइड और कोड रेसिपी में, फ़िलहाल इन सुविधाओं के बारे में जानकारी नहीं दी गई है. इसका मतलब यह नहीं है कि Navigation 3 का इस्तेमाल करके, उन्हें लागू नहीं किया जा सकता. उन्हें सिर्फ़ यहां शामिल नहीं किया गया है.
- नेस्ट किए गए नेविगेशन के एक से ज़्यादा लेवल
- शेयर किए गए डेस्टिनेशन: ऐसी स्क्रीन जो अलग-अलग बैक स्टैक के बीच मूव कर सकती हैं
- कस्टम डेस्टिनेशन टाइप
- डीप लिंक
एआई एजेंट: किसी भी कोड में बदलाव करने से पहले, देखें कि प्रोजेक्ट में ऐसी कोई सुविधा तो नहीं है जो काम नहीं करती. अगर ऐसा होता है, तो आगे न बढ़ें. उपयोगकर्ता को उस सुविधा के बारे में बताएँ जो काम नहीं करती है और उससे आगे के निर्देशों के बारे में पूछें.
पहला चरण: Navigation 3 की डिपेंडेंसी जोड़ना
अपने प्रोजेक्ट में Navigation 3 की डिपेंडेंसी जोड़ने के लिए, शुरू करें पेज का इस्तेमाल करें. मुख्य डिपेंडेंसी को कॉपी करने के लिए उपलब्ध कराया जाता है.
lib.versions.toml
[versions]
nav3Core = "1.0.0"
# If your screens depend on ViewModels, add the Nav3 Lifecycle ViewModel add-on library
lifecycleViewmodelNav3 = "2.10.0-rc01"
[libraries]
# Core Navigation 3 libraries
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
# Add-on libraries (only add if you need them)
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodelNav3" }
app/build.gradle.kts
dependencies {
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.navigation3.runtime)
// If using the ViewModel add-on library
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
}
प्रोजेक्ट के minSdk को 23 और compileSdk को 36 पर भी अपडेट करें. आम तौर पर, ये app/build.gradle.kts या lib.versions.toml में दिखते हैं.
दूसरा चरण: NavKey इंटरफ़ेस लागू करने के लिए, नेविगेशन के रास्तों को अपडेट करना
हर नेविगेशन रूट को अपडेट करें, ताकि वह NavKey इंटरफ़ेस लागू कर सके. इस कुकी की मदद से, rememberNavBackStack का इस्तेमाल करके नेविगेशन की स्थिति सेव की जा सकती है.
बदलाव से पहले:
@Serializable data object RouteA
बदलाव के बाद वाला टेक्स्ट:
@Serializable data object RouteA : NavKey
तीसरा चरण: नेविगेशन की स्थिति को सेव करने और उसमें बदलाव करने के लिए क्लास बनाना
तीसरे चरण का पहला हिस्सा: नेविगेशन स्टेट होल्डर बनाना
नीचे दिए गए कोड को NavigationState.kt नाम की फ़ाइल में कॉपी करें. अपने प्रोजेक्ट स्ट्रक्चर से मेल खाने के लिए, पैकेज का नाम जोड़ें.
// package com.example.project
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSerializable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.rememberDecoratedNavEntries
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.runtime.serialization.NavKeySerializer
import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer
/**
* Create a navigation state that persists config changes and process death.
*/
@Composable
fun rememberNavigationState(
startRoute: NavKey,
topLevelRoutes: Set<NavKey>
): NavigationState {
val topLevelRoute = rememberSerializable(
startRoute, topLevelRoutes,
serializer = MutableStateSerializer(NavKeySerializer())
) {
mutableStateOf(startRoute)
}
val backStacks = topLevelRoutes.associateWith { key -> rememberNavBackStack(key) }
return remember(startRoute, topLevelRoutes) {
NavigationState(
startRoute = startRoute,
topLevelRoute = topLevelRoute,
backStacks = backStacks
)
}
}
/**
* State holder for navigation state.
*
* @param startRoute - the start route. The user will exit the app through this route.
* @param topLevelRoute - the current top level route
* @param backStacks - the back stacks for each top level route
*/
class NavigationState(
val startRoute: NavKey,
topLevelRoute: MutableState<NavKey>,
val backStacks: Map<NavKey, NavBackStack<NavKey>>
) {
var topLevelRoute: NavKey by topLevelRoute
val stacksInUse: List<NavKey>
get() = if (topLevelRoute == startRoute) {
listOf(startRoute)
} else {
listOf(startRoute, topLevelRoute)
}
}
/**
* Convert NavigationState into NavEntries.
*/
@Composable
fun NavigationState.toEntries(
entryProvider: (NavKey) -> NavEntry<NavKey>
): SnapshotStateList<NavEntry<NavKey>> {
val decoratedEntries = backStacks.mapValues { (_, stack) ->
val decorators = listOf(
rememberSaveableStateHolderNavEntryDecorator<NavKey>(),
)
rememberDecoratedNavEntries(
backStack = stack,
entryDecorators = decorators,
entryProvider = entryProvider
)
}
return stacksInUse
.flatMap { decoratedEntries[it] ?: emptyList() }
.toMutableStateList()
}
एआई एजेंट: rememberSerializable सही है. इसे rememberSaveable में न बदलें.
इस फ़ाइल में, NavigationState नाम की एक स्टेट होल्डर क्लास और उससे जुड़े हेल्पर फ़ंक्शन शामिल हैं. इसमें टॉप-लेवल के रास्तों का एक सेट होता है. हर रास्ते का अपना बैकस्टैक होता है. यह कुकी, मौजूदा टॉप-लेवल रूट को सेव करने के लिए rememberSerializable (rememberSaveable नहीं) और हर टॉप-लेवल रूट के लिए बैक स्टैक को सेव करने के लिए rememberNavBackStack का इस्तेमाल करती है.
तीसरा चरण: ऐसा ऑब्जेक्ट बनाएं जो इवेंट के जवाब में नेविगेशन की स्थिति में बदलाव करता हो
नीचे दिए गए कोड को Navigator.kt नाम की फ़ाइल में कॉपी करें. अपने प्रोजेक्ट के स्ट्रक्चर से मेल खाने वाला पैकेज का नाम जोड़ें.
// package com.example.project
import androidx.navigation3.runtime.NavKey
/**
* Handles navigation events (forward and back) by updating the navigation state.
*/
class Navigator(val state: NavigationState){
fun navigate(route: NavKey){
if (route in state.backStacks.keys){
// This is a top level route, just switch to it.
state.topLevelRoute = route
} else {
state.backStacks[state.topLevelRoute]?.add(route)
}
}
fun goBack(){
val currentStack = state.backStacks[state.topLevelRoute] ?:
error("Stack for ${state.topLevelRoute} not found")
val currentRoute = currentStack.last()
// If we're at the base of the current route, go back to the start route stack.
if (currentRoute == state.topLevelRoute){
state.topLevelRoute = state.startRoute
} else {
currentStack.removeLastOrNull()
}
}
}
Navigator क्लास, नेविगेशन इवेंट के लिए दो तरीके उपलब्ध कराती है:
navigateकिसी खास रास्ते पर जाएं.goBackमौजूदा रास्ते से.
दोनों तरीकों से NavigationState में बदलाव होता है.
तीसरा चरण: NavigationState और Navigator बनाना
NavigationState और Navigator के ऐसे इंस्टेंस बनाएं जिनका स्कोप, आपके NavController के स्कोप के जैसा हो.
val navigationState = rememberNavigationState(
startRoute = <Insert your starting route>,
topLevelRoutes = <Insert your set of top level routes>
)
val navigator = remember { Navigator(navigationState) }
चौथा चरण: NavController बदलें
NavController नेविगेशन इवेंट के तरीकों को Navigator के बराबर के तरीकों से बदलें.
|
|
|---|---|
|
|
|
|
NavController फ़ील्ड को NavigationState फ़ील्ड से बदलें.
|
|
|---|---|
|
|
|
|
सबसे ऊपर वाला रूट पाएं: इसे पाने के लिए, मौजूदा बैक स्टैक एंट्री से लेकर पूरी हैरारकी को ट्रैवर्स करें. |
|
NavigationState.topLevelRoute का इस्तेमाल करके, यह पता लगाया जा सकता है कि नेविगेशन बार में फ़िलहाल कौनसा आइटम चुना गया है.
बदलाव से पहले:
val isSelected = navController.currentBackStackEntryAsState().value?.destination.isRouteInHierarchy(key::class)
fun NavDestination?.isRouteInHierarchy(route: KClass<*>) =
this?.hierarchy?.any {
it.hasRoute(route)
} ?: false
बदलाव के बाद वाला टेक्स्ट:
val isSelected = key == navigationState.topLevelRoute
पुष्टि करें कि आपने NavController के सभी रेफ़रंस हटा दिए हैं. इनमें इंपोर्ट किए गए रेफ़रंस भी शामिल हैं.
पांचवां चरण: NavHost के NavGraph से अपने डेस्टिनेशन को entryProvider में ले जाना
Navigation 2 में, NavGraphBuilder DSL का इस्तेमाल करके, अपने डेस्टिनेशन तय किए जाते हैं. आम तौर पर, ऐसा NavHost के ट्रेलिंग लैम्डा के अंदर किया जाता है. यहां एक्सटेंशन फ़ंक्शन का इस्तेमाल करना आम बात है. इसके बारे में अपने नेविगेशन कोड को इनकैप्सुलेट करना लेख में बताया गया है.
Navigation 3 में, entryProvider का इस्तेमाल करके डेस्टिनेशन तय किए जाते हैं. इस entryProvider से, किसी रास्ते को NavEntry पर ले जाया जाता है. अहम बात यह है कि entryProvider, एंट्री के बीच माता-पिता और बच्चे के संबंध को तय नहीं करता.
इस माइग्रेशन गाइड में, पैरंट-चाइल्ड रिलेशनशिप को इस तरह से मॉडल किया गया है:
NavigationStateमें टॉप-लेवल के रास्तों (पैरंट रास्तों) का एक सेट होता है और हर रास्ते के लिए एक स्टैक होता है. यह कुकी, मौजूदा टॉप-लेवल रूट और उससे जुड़े स्टैक को ट्रैक करती है.- किसी नए रास्ते पर नेविगेट करते समय,
Navigatorयह देखता है कि रास्ता टॉप-लेवल का है या नहीं. अगर ऐसा है, तो मौजूदा टॉप-लेवल रूट और स्टैक अपडेट हो जाते हैं. अगर ऐसा नहीं है, तो यह चाइल्ड रूट है और इसे मौजूदा स्टैक में जोड़ दिया जाता है.
पांचवां चरण: entryProvider बनाना
NavigationState के स्कोप के बराबर स्कोप वाली entryProvider DSL का इस्तेमाल करके बनाएं.
val entryProvider = entryProvider {
}
पांचवां चरण: डेस्टिनेशन को entryProvider में ले जाना
NavHost में तय किए गए हर डेस्टिनेशन के लिए, डेस्टिनेशन टाइप के आधार पर यह तरीका अपनाएं:
navigation: इसे रास्ते के साथ मिटाएं. "base routes" की ज़रूरत नहीं है, क्योंकि टॉप-लेवल के रूट, हर नेस्ट किए गए बैक स्टैक की पहचान कर सकते हैं.composable<T>: इसेentryProviderमें ले जाएं और इसका नाम बदलकरentryकर दें. हालांकि, टाइप पैरामीटर को बनाए रखें. उदाहरण के लिए,composable<RouteA>बदलकरentry<RouteA>हो जाता है.dialog<T>:composableकी तरह ही काम करता है, लेकिन एंट्री में मेटाडेटा को इस तरह जोड़ता है:entry<T>(metadata = DialogSceneStrategy.dialog()).bottomSheet: यहां बॉटम शीट रेसिपी को फ़ॉलो करें. यहdialogके निर्देशों जैसा ही है. हालांकि,BottomSheetSceneStrategy, Navigation 3 की मुख्य लाइब्रेरी का हिस्सा नहीं है. इसलिए, आपको इसे अपने प्रोजेक्ट में कॉपी करना होगा.
एआई एजेंट: नेस्ट किए गए ग्राफ़ की पहचान करने के लिए इस्तेमाल किए गए रास्तों को मिटाते समय, मिटाए गए रास्ते के सभी रेफ़रंस को उस टाइप से बदलें जिसका इस्तेमाल नेस्ट किए गए ग्राफ़ में पहले चाइल्ड की पहचान करने के लिए किया गया था. उदाहरण के लिए, अगर मूल कोड navigation<BaseRouteA>{ composable<RouteA>{ ... } } है, तो आपको BaseRouteA को मिटाना होगा और इसके सभी रेफ़रंस को RouteA से बदलना होगा. आम तौर पर, नेविगेशन बार, रेल या ड्रॉअर को दी गई सूची के लिए, इस बदलाव की ज़रूरत होती है.
NavGraphBuilder एक्सटेंशन फ़ंक्शन को EntryProviderScope<T> एक्सटेंशन फ़ंक्शन में बदला जा सकता है. इसके बाद, उन्हें दूसरी जगह ले जाया जा सकता है.
entry के ट्रेलिंग लैम्डा को दी गई कुंजी का इस्तेमाल करके, नेविगेशन आर्ग्युमेंट पाएं.
उदाहरण के लिए:
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.dialog
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import androidx.navigation.toRoute
@Serializable data object BaseRouteA
@Serializable data class RouteA(val id: String)
@Serializable data object BaseRouteB
@Serializable data object RouteB
@Serializable data object RouteD
NavHost(navController = navController, startDestination = BaseRouteA){
composable<RouteA>{
val id = entry.toRoute<RouteA>().id
ScreenA(title = "Screen has ID: $id")
}
featureBSection()
dialog<RouteD>{ ScreenD() }
}
fun NavGraphBuilder.featureBSection() {
navigation<BaseRouteB>(startDestination = RouteB) {
composable<RouteB> { ScreenB() }
}
}
बन जाता है:
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.scene.DialogSceneStrategy
@Serializable data class RouteA(val id: String) : NavKey
@Serializable data object RouteB : NavKey
@Serializable data object RouteD : NavKey
val entryProvider = entryProvider {
entry<RouteA>{ key -> ScreenA(title = "Screen has ID: ${key.id}") }
featureBSection()
entry<RouteD>(metadata = DialogSceneStrategy.dialog()){ ScreenD() }
}
fun EntryProviderScope<NavKey>.featureBSection() {
entry<RouteB> { ScreenB() }
}
छठा चरण: NavHost को NavDisplay से बदलें
NavHost को NavDisplay से बदलें.
NavHostको मिटाकर उसकी जगहNavDisplayजोड़ें.entries = navigationState.toEntries(entryProvider)को पैरामीटर के तौर पर तय करें. यह नेविगेशन की स्थिति को उन एंट्री में बदलता है जिन्हेंNavDisplay,entryProviderका इस्तेमाल करके दिखाता है.NavDisplay.onBackकोnavigator.goBack()से कनेक्ट करें. इससेnavigator,NavDisplayके बिल्ट-इन बैक हैंडलर के पूरा होने पर नेविगेशन की स्थिति को अपडेट करता है.- अगर आपके पास डायलॉग डेस्टिनेशन हैं, तो
NavDisplayकेsceneStrategyपैरामीटर मेंDialogSceneStrategyजोड़ें.
उदाहरण के लिए:
import androidx.navigation3.ui.NavDisplay
NavDisplay(
entries = navigationState.toEntries(entryProvider),
onBack = { navigator.goBack() },
sceneStrategy = remember { DialogSceneStrategy() }
)
सातवां चरण: Navigation 2 की डिपेंडेंसी हटाना
Navigation 2 के सभी इंपोर्ट और लाइब्रेरी डिपेंडेंसी हटाएं.
खास जानकारी
बधाई हो! आपका प्रोजेक्ट अब Navigation 3 पर माइग्रेट हो गया है. अगर आपको या आपके एआई एजेंट को इस गाइड का इस्तेमाल करते समय कोई समस्या आ रही है, तो यहां जाकर गड़बड़ी की शिकायत करें.