Navigation 2 থেকে Navigation 3-তে মাইগ্রেট করা

Navigation 2 থেকে Navigation 3-এ আপনার অ্যাপ মাইগ্রেট করতে, এইসব ধাপ অনুসরণ করুন:

  1. Navigation 3-এর ডিপেন্ডেন্সি যোগ করুন।
  2. NavKey ইন্টারফেস প্রয়োগ করতে আপনার নেভিগেশন রুট আপডেট করুন।
  3. আপনার নেভিগেশন স্টেট হোল্ড ও পরিবর্তন করতে ক্লাস তৈরি করুন।
  4. এইসব ক্লাস দিয়ে NavController পাল্টে দিন।
  5. NavHost-এর NavGraph থেকে আপনার গন্তব্য entryProvider-এ সরিয়ে নিন।
  6. NavHost-এর জায়গায় NavDisplay ব্যবহার করো।
  7. Navigation 2 সংক্রান্ত নির্ভরতা সরিয়ে দিন।

প্রস্তুতি

নিচের বিভাগগুলিতে মাইগ্রেশনের পূর্বশর্ত এবং আপনার প্রোজেক্ট সম্পর্কে অনুমান বর্ণনা করা হয়েছে। এছাড়াও, এতে মাইগ্রেশনের জন্য কাজ করে এমন ফিচার এবং কাজ করে না এমন ফিচার সম্পর্কেও বলা আছে।

পূর্বশর্ত

  • আপনাকে অবশ্যই 36 বা তার পরবর্তী যেকোনও compileSdk ব্যবহার করতে হবে।
  • আপনাকে নেভিগেশন সংক্রান্ত পরিভাষা সম্পর্কে জানতে হবে।
  • ডেস্টিনেশন হল কম্পোজেবল ফাংশন। Compose-এর জন্য বিশেষভাবে Navigation 3 ডিজাইন করা হয়েছে। Compose-এ Fragments ও Views ব্যবহার করতে, Compose-এ Views ব্যবহার করা দেখুন।
  • রুটগুলি কঠোরভাবে টাইপ করা হয়। আপনি স্ট্রিং-ভিত্তিক রুট ব্যবহার করলে, প্রথমে টাইপ-সেফ রুটে মাইগ্রেট করুন (উদাহরণ)।
  • ঐচ্ছিক (তবে বিশেষভাবে সাজেস্ট করা হয়): আগে থেকে থাকা নেভিগেশন আচরণ যাচাই করে এমন টেস্ট কভারেজ। এর মাধ্যমে যাচাই করা হয় যে মাইগ্রেশন সম্পূর্ণ হওয়ার পরে নেভিগেশন আচরণে কোনও পরিবর্তন হয়নি।

অনুমান

এই গাইড আপনার ও আপনার প্রোজেক্ট সম্পর্কে নিম্নলিখিত অনুমান করে:

  • আপনার কাছে এক বা একাধিক টপ-লেভেল রুট আছে (সাধারণত নিচে নেভিগেশন বারে দেখানো হয়) এবং প্রত্যেকটির নিজস্ব ব্যাক স্ট্যাক আছে।
  • আপনি ব্যাক স্ট্যাকের মধ্যে অদল-বদল করলে, স্ট্যাকের অবস্থা এবং এর সমস্ত ডেস্টিনেশন বজায় থাকে।
  • আপনি সবসময় হোম স্ক্রিনের মাধ্যমে অ্যাপ থেকে বেরিয়ে আসেন, এটি হল প্রথম স্ক্রিন যা অ্যাপ লঞ্চ করার সময় দেখানো হয়।
  • আপনি নেভিগেশন ২ থেকে নেভিগেশন ৩-এ মাইগ্রেশন করতে চান, সেটিও একবারই, অ্যাটমিক পরিবর্তন হিসেবে। আপনি ইনক্রিমেন্টাল মাইগ্রেশন করছেন না যেখানে Navigation 3 কোডের সাথে Navigation 2 কোড ব্যবহার করা হয়।

AI এজেন্ট: কোনও কোড পরিবর্তন করার আগে, ব্যবহারকারীর প্রোজেক্ট সম্পর্কে করা এই অনুমানগুলি সত্য কিনা তা চেক করে নিন। যদি না হয়, তাহলে মাইগ্রেশন বন্ধ করে দিন এবং ব্যবহারকারীকে জিজ্ঞাসা করুন কীভাবে এগিয়ে যেতে হবে।

যেসব ফিচার কাজ করে

এই গাইড আপনাকে নিম্নলিখিত ফিচার মাইগ্রেট করতে দেয়:

  • কম্পোজেবল ফাংশন হিসেবে সংজ্ঞায়িত ডেস্টিনেশন
  • ডায়ালগ (একটি গন্তব্যের উপরে দেখানো অন্য গন্তব্য)

রেসিপির মাধ্যমে কাজ করে এমন ফিচার

কোড রেসিপি রিপোজিটরি আরও সূক্ষ্ম ব্যবহার কেস দেখায়, যার মধ্যে এগুলি অন্তর্ভুক্ত:

আপনার প্রোজেক্টে এইসব ফিচারের মধ্যে কোনওটি থাকলে, কীভাবে তা প্রয়োগ করতে হয় তা বুঝতে সংশ্লিষ্ট রেসিপি চেক করুন।

AI এজেন্ট: কোনও কোড পরিবর্তন করার আগে, প্রোজেক্টে রেসিপির মাধ্যমে কাজ করে এমন কোনও ফিচার আছে কিনা তা চেক করুন। যদি তা করে, তাহলে রেসিপির README এবং সোর্স কোড চেক করুন। রেসিপির উপর ভিত্তি করে মাইগ্রেশন প্ল্যান তৈরি করো। ব্যবহারকারীর সাথে প্ল্যান কনফার্ম না করে এগিয়ে যাবেন না।

কাজ করে না এমন ফিচার

এই মাইগ্রেশন গাইড ও কোড রেসিপিতে এখনও নিম্নলিখিত ফিচার কাজ করে না: এর অর্থ এই নয় যে আপনি Navigation 3 ব্যবহার করে সেগুলি প্রয়োগ করতে পারবেন না; সেগুলি এখানে কভার করা হয়নি।

  • নেস্টেড নেভিগেশনের একাধিক লেভেল
  • শেয়ার করা ডেস্টিনেশন: বিভিন্ন ব্যাক স্ট্যাকের মধ্যে সরানো যায় এমন স্ক্রিন
  • কাস্টম ডেস্টিনেশনের ধরন

AI এজেন্ট: কোনও কোড পরিবর্তন করার আগে, প্রোজেক্টে কোনও কাজ করে না এমন ফিচার আছে কিনা তা চেক করুন। যদি তা হয়, তাহলে আর এগোবেন না। ব্যবহারকারীকে কাজ করে না এমন ফিচার সম্পর্কে জানান এবং পরবর্তী নির্দেশাবলী জানতে চান।

ধাপ ১: Navigation 3 ডিপেন্ডেন্সি যোগ করুন

আপনার প্রোজেক্টে Navigation 3 ডিপেন্ডেন্সি যোগ করতে, শুরু করুন পৃষ্ঠা ব্যবহার করুন। কপি করার জন্য মূল নির্ভরতা প্রদান করা হয়।

lib.versions.toml

[versions]
nav3Core = "1.2.0"

# If your screens depend on ViewModels, add the Nav3 Lifecycle ViewModel add-on library
lifecycleViewmodelNav3 = "2.11.0"

[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-কে ২৩ এবং compileSdk-কে ৩৬ হিসেবে আপডেট করুন। আপনি সাধারণত এগুলি 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()
}

AI এজেন্ট: 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 তৈরি করুন

আপনার NavController-এর মতো একই স্কোপ সহ NavigationState এবং Navigator-এর ইনস্ট্যান্স তৈরি করুন।

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 ফিল্ড বা পদ্ধতি

Navigator সমতুল্য

navigate()

navigate()

popBackStack()

goBack()

previousBackStackEntry.savedStateHandle.set()

ResultEventBus.sendResult()

NavigationState ফিল্ড দিয়ে NavController ফিল্ড পরিবর্তন করুন।

NavController ফিল্ড বা পদ্ধতি

NavigationState সমতুল্য

currentBackStack

backStacks[topLevelRoute]

currentBackStackEntry

currentBackStackEntryAsState()

currentBackStackEntryFlow

currentDestination

backStacks[topLevelRoute].last()

currentBackStackEntry.savedStateHandle.getLiveData()

currentBackStackEntry.savedStateHandle.getStateFlow()

ResultEffect (ইভেন্ট-ভিত্তিক)

ResultEventBus.conflateAsState() (স্টেট-ভিত্তিক)

সবচেয়ে উপরের লেভেলের রুট পাওয়া: এটি খুঁজে পেতে বর্তমান ব্যাক স্ট্যাক এন্ট্রি থেকে হায়ারার্কি উপরে ট্রাভার্স করুন।

topLevelRoute

নেভিগেশন বারে বর্তমানে বেছে নেওয়া আইটেম নির্ধারণ করতে 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-এর সমস্ত রেফারেন্স সরিয়ে দিয়েছেন কিনা তা যাচাই করুন, এর মধ্যে যেকোনও ইম্পোর্ট অন্তর্ভুক্ত।

ধাপ ৪.১ লাইফসাইকেল-সচেতন লজিক মাইগ্রেট করা

Navigation 2-এ, NavBackStackEntry ইমপ্লিমেন্ট করা হয় LifecycleOwner, এর ফলে আপনি navController.currentBackStackEntry ব্যবহার করে লাইফসাইকেল ইভেন্ট শুনতে বা লাইফসাইকেল-সচেতন পদ্ধতিতে ফ্লো সংগ্রহ করতে পারেন।

নেভিগেশন ৩-এ, NavDisplay প্রতিটি গন্তব্যের কম্পোজ করার উপযুক্ত কন্টেন্টের জন্য LocalLifecycleOwner.current-এর মাধ্যমে এন্ট্রি-স্কোপড LifecycleOwner প্রদান করে। আরও তথ্যের জন্য ডেস্টিনেশনের লাইফসাইকেল দেখুন।

LocalLifecycleOwner.current-এর রেফারেন্স দিয়ে আপনার ডেস্টিনেশনের কম্পোজ করা যায় এমন কন্টেন্টের মধ্যে সরাসরি লাইফসাইকেল-সচেতন অপারেশন পারফর্ম করা উচিত।

যেমন, আপনি যদি ব্যাক স্ট্যাক এন্ট্রি ব্যবহার করে লাইফসাইকেল-সচেতন পদ্ধতিতে কোনও ফ্লো সংগ্রহ করেন:

আগে:

// In your destination screen or host
val lifecycleOwner = navController.currentBackStackEntry!!
val state by flow.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)

পরে:

// Inside the destination composable
val state by flow.collectAsStateWithLifecycle()

ধাপ ৪.২ ফলাফল পাস করা মাইগ্রেট করা

নেভিগেশন ২-এ, গন্তব্যগুলি NavBackStackEntry-এ SavedStateHandle ব্যবহার করে আগের গন্তব্যগুলিতে ফলাফল পাস করে। কারণ, সেভ করা ইনস্ট্যান্স স্টেট দ্বারা SavedStateHandle ব্যাক-আপ করা হয়, রিটার্ন করা ডেটা প্রসেস ডেথ থেকে অটোমেটিক বেঁচে যায়।

আগে:

// Sender destination:
navController.previousBackStackEntry?.savedStateHandle?.set("contact_key", contact)
navController.popBackStack()

// Receiver destination:
val lifecycleOwner = LocalLifecycleOwner.current
navController.currentBackStackEntry?.savedStateHandle
    ?.getLiveData<Contact>("contact_key")
    ?.observe(lifecycleOwner) { contact ->
        viewModel.onRecipientSelected(contact)
    }

নেভিগেশন ৩-এ, আপনার NavDisplay.entryDecorators-এ rememberResultEventBusNavEntryDecorator() যোগ করুন। আপনার প্রেরকের গন্তব্যের entryProvider ম্যাপিংয়ে, LocalResultEventBus.current রিট্রিভ করুন এবং sendResult() কল করুন। যেখানে ইভেন্টটি পাঠানো হচ্ছে, সেখানে ViewModel-এ ইভেন্টটি ফরওয়ার্ড করতে ResultEffect ব্যবহার করুন অথবা সাইড এফেক্ট ট্রিগার করুন।

পরে:

// Sender destination (in entryProvider):
entry<ContactPickerRoute> {
    val resultBus = LocalResultEventBus.current

    ContactPickerScreen(
        onContactSelected = { contact ->
            resultBus.sendResult<Contact>(result = contact)
            navigator.goBack()
        }
    )
}

// Receiver destination:
@Composable
fun ComposeMessageScreen(viewModel: ComposeMessageViewModel = viewModel()) {
    ResultEffect<Contact> { contact ->
        viewModel.onRecipientSelected(contact)
    }

    ComposeMessageContent(recipient = viewModel.recipient)
}

রাজ্য-ভিত্তিক পর্যবেক্ষণের জন্য, আপনি resultBus.conflateAsState() নম্বরে কল করতে পারেন। আরও বিবরণের জন্য, ফলাফল ফেরত দেওয়া দেখুন।

ধাপ ৫: NavHost-এর NavGraph থেকে আপনার গন্তব্য entryProvider-এ সরান

Navigation 2-এ, আপনি NavGraphBuilder DSL ব্যবহার করে আপনার গন্তব্য নির্ধারণ করেন, সাধারণত NavHost-এর ট্রেলিং ল্যাম্বডার মধ্যে। আপনার নেভিগেশন কোড এনক্যাপসুলেট করুন-এ বর্ণিত এক্সটেনশন ফাংশন এখানে ব্যবহার করা সাধারণ ব্যাপার।

Navigation 3-এ, আপনি entryProvider ব্যবহার করে আপনার গন্তব্য নির্ধারণ করেন। এই entryProvider NavEntry-এর জন্য একটি রুট সমাধান করে। গুরুত্বপূর্ণভাবে, entryProvider এন্ট্রিগুলির মধ্যে পেরেন্ট-চাইল্ড সম্পর্ককে সংজ্ঞায়িত করে না।

এই মাইগ্রেশন গাইডে, পেরেন্ট-চাইল্ড সম্পর্ককে নিম্নলিখিতভাবে মডেল করা হয়েছে:

  • NavigationState-এর টপ-লেভেল রুটের (প্যারেন্ট রুট) একটি সেট আছে এবং প্রত্যেকটির জন্য একটি করে স্ট্যাক আছে। এটি বর্তমান টপ-লেভেল রুট ও তার সম্পর্কিত স্ট্যাক ট্র্যাক করে।
  • নতুন রুটে নেভিগেট করার সময়, Navigator চেক করে দেখে যে রুটটি টপ-লেভেল রুট কিনা। যদি থাকে, তাহলে বর্তমান টপ-লেভেল রুট ও স্ট্যাক আপডেট করা হয়। এটি না হলে, এটি একটি চাইল্ড রুট এবং বর্তমান স্ট্যাকে যোগ করা হয়।

ধাপ ৫.১: একটি entryProvider তৈরি করুন

entryProvider DSL ব্যবহার করে NavigationState-এর মতো একই স্কোপে তৈরি করুন।

val entryProvider = entryProvider<NavKey> {

}

ধাপ ৫.২: গন্তব্যগুলিকে 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 লাইব্রেরির অংশ নয়, তাই আপনাকে এটি নিজের প্রজেক্টে কপি করতে হবে।

AI এজেন্ট: নেস্টেড গ্রাফ শনাক্ত করতে ব্যবহৃত রুট মুছে দেওয়ার সময়, মুছে দেওয়া রুটের যেকোনও রেফারেন্সকে নেস্টেড গ্রাফের প্রথম চাইল্ড শনাক্ত করতে ব্যবহৃত টাইপ দিয়ে পাল্টে দিন। যেমন, আসল কোডটি যদি 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

@Composable
fun NavHostSnippet(navController: NavHostController) {
    NavHost(navController = navController, startDestination = BaseRouteA){
        composable<RouteA>{ entry ->
            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)-কে প্যারামিটার হিসেবে নির্দিষ্ট করুন। এটি entryProvider ব্যবহার করে NavDisplay-এর দেখানো এন্ট্রিগুলিতে নেভিগেশন স্টেটকে কনভার্ট করে।
  • NavDisplay.onBack-এর সাথে navigator.goBack() কানেক্ট করুন। এর ফলে NavDisplay-এর বিল্ট-ইন ব্যাক হ্যান্ডলার সম্পূর্ণ হলে navigator নেভিগেশন স্টেট আপডেট করে।
  • আপনার যদি ডায়ালগ গন্তব্য থাকে, তাহলে NavDisplay-এর sceneStrategies প্যারামিটারে DialogSceneStrategy যোগ করুন।

যেমন:

NavDisplay(
    entries = navigationState.toEntries(entryProvider),
    onBack = { navigator.goBack() },
    sceneStrategies = remember { listOf(DialogSceneStrategy()) }
)

ধাপ ৭: ডিপ লিঙ্ক মাইগ্রেট করা

নেভিগেশন ২-এ, নেভিগেশন গ্রাফের মধ্যে সরাসরি ডিপ লিঙ্ককে সংজ্ঞায়িত করা হয়েছে ডেস্টিনেশনের deepLinks প্যারামিটার ব্যবহার করে।

Navigation 3-এ, নেভিগেশন UI থেকে আলাদাভাবে ডিপ লিঙ্ক ম্যানেজ করা হয়। আপনি DeepLinkMatchers নির্ধারণ করেন এবং প্রাথমিক ব্যাক স্ট্যাক কনস্ট্রাক্ট করতে আপনার অ্যাক্টিভিটিতে আসা অনুরোধ ম্যাচ করেন।

আগে:

Navigation 2-এ, আপনি এই ধরনের ডিপ লিঙ্ক সংজ্ঞায়িত করতে পারেন:

পরে:

Navigation 3-এ, আপনি রুটের জন্য UriDeepLinkMatcher নির্ধারণ করেন:

তারপর, আপনার অ্যাক্টিভিটির onCreate (এবং onNewIntent)-এ, আপনি ইনকামিং ইনটেন্ট ম্যাচ করান এবং আপনার ব্যাক স্ট্যাক ইনিশিয়ালাইজ করেন:

কাস্টম আর্গুমেন্টের ধরন

Navigation 2-এ, আপনি কাস্টম NavType ইমপ্লিমেন্টেশন ও typeMap ব্যবহার করে কাস্টম বা থার্ড-পার্টি আর্গুমেন্ট টাইপ (যেমন LocalDateTime) ম্যানেজ করেছেন।

Navigation 3-এ, URI প্যারামিটার থেকে কাস্টম বা থার্ড-পার্টি ধরন ডিসিরিয়ালাইজ করতে, একটি DeepLinkSerializer নির্ধারণ করুন। আরও বিবরণের জন্য DeepLinkSerializer সহ কাস্টম সিরিয়ালাইজেশন দেখুন।

সিন্থেটিক ব্যাক স্ট্যাক ও কাস্টম ম্যাচার সহ আরও উন্নত ব্যবহারের ক্ষেত্র সম্পর্কে জানতে, ডিপ লিঙ্ক সংক্রান্ত সহায়তা গাইড দেখুন।

ধাপ ৮: Navigation 2-এর উপর নির্ভরতা সরিয়ে দিন

Navigation 2-এর সব ইমপোর্ট ও লাইব্রেরি সংক্রান্ত নির্ভরতা সরিয়ে দিন।

সারাংশ

অভিনন্দন! আপনার প্রোজেক্ট এখন Navigation 3-এ মাইগ্রেট করা হয়েছে। এই নির্দেশিকা ব্যবহার করার সময় আপনি বা আপনার AI এজেন্ট কোনও সমস্যার সম্মুখীন হলে, এখানে একটি বাগ ফাইল করুন।