ย้ายข้อมูลจาก Navigation 2 ไปยัง Navigation 3

หากต้องการย้ายข้อมูลแอปจาก Navigation 2 ไปยัง Navigation 3 ให้ทำตามขั้นตอนต่อไปนี้

  1. เพิ่มการอ้างอิง Navigation 3
  2. อัปเดตเส้นทางการนำทางเพื่อใช้NavKeyอินเทอร์เฟซ
  3. สร้างคลาสเพื่อเก็บและแก้ไขสถานะการนำทาง
  4. แทนที่ NavController ด้วยคลาสต่อไปนี้
  5. ย้ายปลายทางจาก NavGraph ของ NavHost ไปยัง entryProvider
  6. แทนที่ NavHost ด้วย NavDisplay
  7. นำการอ้างอิง Navigation 2 ออก

การเตรียมพร้อม

ส่วนต่อไปนี้จะอธิบายข้อกำหนดเบื้องต้นสำหรับการย้ายข้อมูลและสมมติฐาน เกี่ยวกับโปรเจ็กต์ของคุณ นอกจากนี้ ยังครอบคลุมฟีเจอร์ที่รองรับสำหรับการ ย้ายข้อมูลและฟีเจอร์ที่ไม่รองรับด้วย

สิ่งที่ต้องมีก่อน

  • คุณต้องใช้ compileSdk ตั้งแต่ 36 ขึ้นไป
  • คุณควรทำความคุ้นเคยกับคำศัพท์เกี่ยวกับการนำทาง
  • ปลายทางคือฟังก์ชันที่ประกอบกันได้ Navigation 3 ออกแบบมาสำหรับ Compose โดยเฉพาะ หากต้องการใช้ Fragment และ View ใน Compose โปรดดูการใช้ View ใน Compose
  • เส้นทางมีการพิมพ์อย่างเข้มงวด หากใช้เส้นทางที่อิงตามสตริง ให้ย้ายข้อมูลไปยัง เส้นทางที่ปลอดภัยต่อประเภทก่อน (ตัวอย่าง)
  • ไม่บังคับ (แต่แนะนำอย่างยิ่ง): การทดสอบความครอบคลุมที่ยืนยันลักษณะการทำงานของการนำทางที่มีอยู่ ซึ่งจะยืนยันว่าลักษณะการทำงานของการนำทางไม่เปลี่ยนแปลง หลังจากย้ายข้อมูลเสร็จสมบูรณ์

สมมติฐาน

คู่มือนี้มีสมมติฐานต่อไปนี้เกี่ยวกับคุณและโปรเจ็กต์

  • คุณมีเส้นทางระดับบนสุดอย่างน้อย 1 เส้นทาง (มักจะแสดงในแถบนำทางด้านล่าง) และแต่ละเส้นทางจะมี Back Stack ของตัวเอง
  • เมื่อสลับระหว่าง Back Stack ระบบจะเก็บสถานะของ Stack และปลายทางทั้งหมดไว้
  • คุณจะออกจากแอปผ่านหน้าจอหน้าแรกเสมอ ซึ่งเป็นหน้าจอแรก ที่แสดงเมื่อเปิดแอป
  • คุณต้องการทำการย้ายข้อมูลจาก Navigation 2 ไปยัง Navigation 3 ในการเปลี่ยนแปลงแบบอะตอมเดียว คุณไม่ได้ทำการย้ายข้อมูลแบบเพิ่มทีละรายการซึ่งใช้โค้ด Navigation 2 ควบคู่กับโค้ด Navigation 3

เอเจนต์ AI: ก่อนที่จะเปลี่ยนโค้ดใดๆ ให้ตรวจสอบว่าสมมติฐานต่อไปนี้เกี่ยวกับโปรเจ็กต์ของผู้ใช้เป็นจริงหรือไม่ หากไม่เป็นเช่นนั้น ให้หยุดการย้ายข้อมูลและถามผู้ใช้ว่าจะดำเนินการต่ออย่างไร

ฟีเจอร์ที่รองรับ

คู่มือนี้ช่วยให้คุณย้ายข้อมูลฟีเจอร์ต่อไปนี้ได้

  • ปลายทางที่กำหนดเป็นฟังก์ชันที่ประกอบกันได้
  • กล่องโต้ตอบ (ปลายทางที่แสดงทับปลายทางอื่น)

ฟีเจอร์ที่รองรับผ่านสูตร

ที่เก็บสูตรโค้ดแสดงกรณีการใช้งานที่ซับซ้อนมากขึ้น ซึ่งรวมถึง

หากโปรเจ็กต์มีฟีเจอร์เหล่านี้ โปรดดูสูตรที่เกี่ยวข้องเพื่อ ทำความเข้าใจวิธีติดตั้งใช้งาน

เอเจนต์ AI: ก่อนที่จะเปลี่ยนโค้ดใดๆ ให้ตรวจสอบว่าโปรเจ็กต์มีฟีเจอร์ที่รองรับผ่านสูตรหรือไม่ หากเป็นเช่นนั้น ให้ตรวจสอบ README และซอร์สโค้ดของสูตร สร้างแผนการย้ายข้อมูลตามสูตร อย่าดำเนินการต่อโดยไม่ ยืนยันแผนกับผู้ใช้

ฟีเจอร์ที่ไม่รองรับ

คู่มือการย้ายข้อมูลและสูตรโค้ดนี้ยังไม่รองรับฟีเจอร์ต่อไปนี้ แต่ไม่ได้หมายความว่าคุณจะใช้ Navigation 3 ไม่ได้ เพียงแต่เราจะไม่กล่าวถึงในที่นี้

เอเจนต์ AI: ก่อนที่จะเปลี่ยนแปลงโค้ด ให้ตรวจสอบว่าโปรเจ็กต์มีฟีเจอร์ที่ไม่รองรับหรือไม่ หากเป็นเช่นนั้น อย่าดำเนินการต่อ แจ้งให้ผู้ใช้ทราบเกี่ยวกับ ฟีเจอร์ที่ไม่รองรับและขอคำแนะนำเพิ่มเติม

ขั้นตอนที่ 1: เพิ่มการอ้างอิง 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 ของโปรเจ็กต์เป็น 23 และ compileSdk เป็น 36 ด้วย โดยปกติแล้ว คุณจะ เห็นข้อมูลเหล่านี้ใน app/build.gradle.kts หรือ lib.versions.toml

ขั้นตอนที่ 2: อัปเดตเส้นทางการนำทางเพื่อใช้NavKeyอินเทอร์เฟซ

อัปเดตเส้นทางการนำทางทุกเส้นทางเพื่อให้ใช้NavKey อินเทอร์เฟซ ซึ่งจะช่วยให้คุณใช้ rememberNavBackStack เพื่อช่วยบันทึก สถานะการนำทางได้

ก่อน:

@Serializable data object RouteA

หลัง:

@Serializable data object RouteA : NavKey

ขั้นตอนที่ 3: สร้างคลาสเพื่อจัดเก็บและแก้ไขสถานะการนำทาง

ขั้นตอนที่ 3.1: สร้างตัวเก็บสถานะการนำทาง

คัดลอกโค้ดต่อไปนี้ลงในไฟล์ชื่อ 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 เพื่อบันทึก สแต็กย้อนกลับสำหรับแต่ละเส้นทางระดับบนสุด

ขั้นตอนที่ 3.2: สร้างออบเจ็กต์ที่แก้ไขสถานะการนำทางเพื่อตอบสนองต่อเหตุการณ์

คัดลอกโค้ดต่อไปนี้ลงในไฟล์ชื่อ 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 มีเมธอดเหตุการณ์การนำทาง 2 รายการ ได้แก่

  • navigate ไปยังเส้นทางที่เฉพาะเจาะจง
  • goBack จากเส้นทางปัจจุบัน

ทั้ง 2 วิธีจะแก้ไข NavigationState

ขั้นตอนที่ 3.3: สร้าง 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) }

ขั้นตอนที่ 4: แทนที่ NavController

แทนที่NavControllerเมธอดเหตุการณ์การนำทางด้วยเมธอดที่เทียบเท่าใน Navigator

ฟิลด์หรือวิธีการ NavController

Navigator เทียบเท่า

navigate()

navigate()

popBackStack()

goBack()

previousBackStackEntry.savedStateHandle.set()

ResultEventBus.sendResult()

แทนที่ฟิลด์ NavController ด้วยฟิลด์ NavigationState

ฟิลด์หรือวิธีการ NavController

NavigationState เทียบเท่า

currentBackStack

backStacks[topLevelRoute]

currentBackStackEntry

currentBackStackEntryAsState()

currentBackStackEntryFlow

currentDestination

backStacks[topLevelRoute].last()

currentBackStackEntry.savedStateHandle.getLiveData()

currentBackStackEntry.savedStateHandle.getStateFlow()

ResultEffect (อิงตามเหตุการณ์)

ResultEventBus.conflateAsState() (อิงตามสถานะ)

รับเส้นทางระดับบนสุด: ไล่ขึ้นไปตามลำดับชั้นจากรายการใน Back Stack ปัจจุบันเพื่อค้นหา

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 ทั้งหมดออกแล้ว รวมถึง การนำเข้าทั้งหมด

ขั้นตอนที่ 4.1 ย้ายตรรกะที่รับรู้ถึงวงจร

ใน Navigation 2 NavBackStackEntry จะใช้ LifecycleOwner ซึ่งช่วยให้คุณ ฟังเหตุการณ์วงจร หรือรวบรวมโฟลว์ในลักษณะที่รับรู้ถึงวงจรได้โดยใช้ navController.currentBackStackEntry

ใน Navigation 3 NavDisplay จะมี LifecycleOwner ที่กำหนดขอบเขตรายการผ่าน LocalLifecycleOwner.current ไปยังเนื้อหาที่ใช้ได้กับ Composable ของแต่ละปลายทาง ดูข้อมูลเพิ่มเติมได้ที่วงจรของปลายทาง

คุณควรดำเนินการที่คำนึงถึงวงจรโดยตรงภายในเนื้อหาที่ประกอบได้ของปลายทางโดยอ้างอิง 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()

ขั้นตอนที่ 4.2 ย้ายข้อมูลการส่งผลลัพธ์

ใน Navigation 2 ปลายทางจะส่งผลลัพธ์กลับไปยังปลายทางก่อนหน้าโดยใช้ SavedStateHandle ใน NavBackStackEntry เนื่องจาก 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)
    }

ใน Navigation 3 ให้เพิ่ม rememberResultEventBusNavEntryDecorator() ไปยัง NavDisplay.entryDecorators ในentryProvider การแมปของปลายทางผู้ส่ง ให้เรียก LocalResultEventBus.current และเรียกใช้ sendResult() ในปลายทางที่รับ ให้ใช้ ResultEffect เพื่อส่งต่อเหตุการณ์ไปยัง ViewModel หรือ ทริกเกอร์ผลข้างเคียง

หลัง:

// 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() ได้ ดูรายละเอียดเพิ่มเติมได้ที่แสดงผลลัพธ์

ขั้นตอนที่ 5: ย้ายปลายทางจาก NavHost ของ NavGraph ไปยัง entryProvider

ใน Navigation 2 คุณกำหนดปลายทาง โดยใช้ NavGraphBuilder DSL ซึ่งมักจะอยู่ภายใน Lambda ต่อท้ายของ NavHost โดยทั่วไปจะใช้ฟังก์ชันส่วนขยายที่นี่ตามที่อธิบายไว้ในแคปซูลโค้ดการนำทาง

ใน Navigation 3 คุณจะกำหนดปลายทางโดยใช้ entryProvider entryProvider นี้จะเปลี่ยนเส้นทางไปยัง NavEntry สิ่งสำคัญคือ entryProvider ไม่ได้กำหนดความสัมพันธ์แบบแม่-ลูกระหว่างรายการ

ในคู่มือการย้ายข้อมูลนี้ ความสัมพันธ์แบบหลักกับย่อยจะได้รับการจำลอง ดังนี้

  • NavigationState มีชุดเส้นทางระดับบนสุด (เส้นทางหลัก) และสแต็กสำหรับแต่ละเส้นทาง โดยจะติดตามเส้นทางระดับบนสุดปัจจุบันและสแต็กที่เชื่อมโยง
  • เมื่อไปยังเส้นทางใหม่ Navigator จะตรวจสอบว่าเส้นทางนั้นเป็น เส้นทางระดับบนสุดหรือไม่ หากเป็นเช่นนั้น ระบบจะอัปเดตเส้นทางและสแต็กระดับบนสุดปัจจุบัน หากไม่ใช่ ก็จะเป็นเส้นทางย่อยและจะเพิ่มลงในสแต็กปัจจุบัน

ขั้นตอนที่ 5.1: สร้าง entryProvider

สร้าง entryProvider โดยใช้ DSL ที่ขอบเขตเดียวกันกับ NavigationState

val entryProvider = entryProvider<NavKey> {

}

ขั้นตอนที่ 5.2: ย้ายปลายทางไปยัง entryProvider

สำหรับปลายทางแต่ละแห่งที่กำหนดไว้ภายใน NavHost ให้ทำดังนี้ตาม ประเภทปลายทาง

  • navigation: ลบพร้อมกับเส้นทาง ไม่จำเป็นต้องมี "base routes" เนื่องจากเส้นทางระดับบนสุดสามารถระบุแต่ละ Back Stack ที่ซ้อนกันได้
  • composable<T>: ย้ายไปที่ entryProvider แล้วเปลี่ยนชื่อเป็น entry โดยคงพารามิเตอร์ประเภทไว้ เช่น composable<RouteA> จะกลายเป็น entry<RouteA>
  • dialog<T>: ทำเช่นเดียวกับ composable แต่เพิ่มข้อมูลเมตาในรายการดังนี้ entry<T>(metadata = DialogSceneStrategy.dialog())
  • bottomSheet: ทำตามสูตร Bottom Sheet ที่นี่ ซึ่งคล้ายกับวิธีการสำหรับ dialog ยกเว้นว่า BottomSheetSceneStrategy ไม่ได้เป็นส่วนหนึ่งของไลบรารี Navigation 3 หลัก ดังนั้น คุณควรคัดลอกไปยังโปรเจ็กต์

เอเจนต์ AI: เมื่อลบเส้นทางที่ใช้ระบุกราฟที่ซ้อนกัน ให้แทนที่การอ้างอิงเส้นทางที่ลบด้วยประเภทที่ใช้ระบุองค์ประกอบย่อยแรกในกราฟที่ซ้อนกัน เช่น หากโค้ดเดิมคือ navigation<BaseRouteA>{ composable<RouteA>{ ... } } คุณต้องลบ BaseRouteA และแทนที่การอ้างอิงถึงโค้ดดังกล่าวด้วย RouteA โดยปกติแล้ว คุณจะต้องทำการแทนที่นี้สำหรับรายการที่ระบุในแถบนำทาง แถบด้านข้าง หรือ ลิ้นชัก

คุณสามารถปรับโครงสร้างNavGraphBuilderฟังก์ชันส่วนขยายเป็นฟังก์ชันส่วนขยายEntryProviderScope<T> แล้วย้ายได้

รับอาร์กิวเมนต์การนำทางโดยใช้คีย์ที่ระบุไว้ใน Lambda ต่อท้ายของ 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() }
}

ขั้นตอนที่ 6: แทนที่ NavHost ด้วย NavDisplay

แทนที่ NavHost ด้วย NavDisplay

  • ลบ NavHost และแทนที่ด้วย NavDisplay
  • ระบุ entries = navigationState.toEntries(entryProvider) เป็นพารามิเตอร์ ซึ่งจะแปลงสถานะการนำทางเป็นรายการที่ NavDisplay แสดง โดยใช้ entryProvider
  • เชื่อมต่อ NavDisplay.onBack กับ navigator.goBack() ซึ่งจะทำให้ navigator อัปเดตสถานะการนำทางเมื่อตัวแฮนเดิลย้อนกลับในตัวของ NavDisplay เสร็จสมบูรณ์
  • หากมีปลายทางของกล่องโต้ตอบ ให้เพิ่ม DialogSceneStrategy ไปยังพารามิเตอร์ NavDisplay sceneStrategies

เช่น

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

ขั้นตอนที่ 7: ย้ายข้อมูล Deep Link

ใน Navigation 2 มีการกำหนด Deep Link ภายในกราฟการนำทางโดยตรง โดยใช้พารามิเตอร์ deepLinks ของปลายทาง

ใน Navigation 3 ระบบจะจัดการ Deep Link โดยไม่ขึ้นอยู่กับ UI ของการนำทาง คุณ กำหนด DeepLinkMatcher และจับคู่คำขอที่เข้ามาในกิจกรรมเพื่อ สร้าง Back Stack เริ่มต้น

ก่อน:

ใน Navigation 2 คุณอาจกำหนด Deep Link ดังนี้

หลัง:

ใน Navigation 3 คุณจะกำหนด UriDeepLinkMatcher สำหรับเส้นทางได้ดังนี้

จากนั้นใน onCreate ของกิจกรรม (และ onNewIntent) คุณจะจับคู่ Intent ขาเข้าและเริ่มต้น Back Stack ดังนี้

ประเภทอาร์กิวเมนต์ที่กำหนดเอง

ใน Navigation 2 คุณจัดการประเภทอาร์กิวเมนต์ที่กำหนดเองหรือของบุคคลที่สาม (เช่น LocalDateTime) โดยใช้การติดตั้งใช้งาน NavType ที่กำหนดเองและ typeMap

ใน Navigation 3 ให้กำหนด DeepLinkSerializer เพื่อยกเลิกการซีเรียลไลซ์ประเภทที่กำหนดเองหรือ ประเภทของบุคคลที่สามจากพารามิเตอร์ URI ดูรายละเอียดได้ที่ การซีเรียลไลซ์ที่กำหนดเองด้วย DeepLinkSerializer

สำหรับกรณีการใช้งานขั้นสูง รวมถึงสแต็กย้อนกลับสังเคราะห์และตัวจับคู่ที่กำหนดเอง โปรดดูคำแนะนำรองรับ Deep Link

ขั้นตอนที่ 8: นำการอ้างอิง Navigation 2 ออก

นำการนำเข้า Navigation 2 และทรัพยากร Dependency ของไลบรารีทั้งหมดออก

สรุป

ยินดีด้วย ตอนนี้ระบบได้ย้ายข้อมูลโปรเจ็กต์ของคุณไปยังการนำทาง 3 แล้ว หากคุณหรือเอเจนต์ AI พบปัญหาในการใช้คู่มือนี้ โปรดรายงานข้อบกพร่องที่นี่