หากต้องการย้ายข้อมูลแอปจาก Navigation 2 ไปยัง Navigation 3 ให้ทำตามขั้นตอนต่อไปนี้
- เพิ่มทรัพยากร Dependency ของ Navigation 3
- อัปเดตเส้นทางการนำทางเพื่อใช้อินเทอร์เฟซ
NavKey - สร้างคลาสเพื่อเก็บและแก้ไขสถานะการนำทาง
- แทนที่
NavControllerด้วยคลาสเหล่านี้ - ย้ายปลายทางจาก
NavGraphของNavHostไปยังentryProvider - แทนที่
NavHostด้วยNavDisplay - นำทรัพยากร Dependency ของ Navigation 2 ออก
พรอมต์ AI สำหรับ
ย้ายข้อมูลจาก Navigation 2 ไปยัง Navigation 3
พรอมต์นี้จะใช้คู่มือนี้เพื่อย้ายข้อมูลไปยัง Navigation 3
Migrate from Navigation 2 to Navigation 3 using the official
migration guide.หากพบปัญหา โปรดรายงานปัญหาที่นี่
การเตรียมพร้อม
ส่วนต่อไปนี้จะอธิบายสิ่งที่ต้องมีก่อนสำหรับการย้ายข้อมูลและสมมติฐานเกี่ยวกับโปรเจ็กต์ รวมถึงฟีเจอร์ที่รองรับการย้ายข้อมูลและฟีเจอร์ที่ไม่รองรับ
สิ่งที่ต้องมีก่อน
- คุณต้องใช้
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: ก่อนที่จะเปลี่ยนโค้ด ให้ตรวจสอบว่าสมมติฐานเหล่านี้เกี่ยวกับ โปรเจ็กต์ของผู้ใช้เป็นจริง หากไม่เป็นจริง ให้หยุดการย้ายข้อมูลและถามผู้ใช้ว่าจะดำเนินการอย่างไรต่อ
ฟีเจอร์ที่รองรับ
คู่มือนี้ช่วยให้คุณย้ายข้อมูลฟีเจอร์ต่อไปนี้ได้
- ปลายทางที่กำหนดเป็นฟังก์ชันที่ใช้ร่วมกันได้
- กล่องโต้ตอบ (ปลายทางที่แสดงอยู่เหนือปลายทางอื่น)
ฟีเจอร์ที่รองรับผ่านสูตร
ที่เก็บสูตรโค้ดแสดงกรณีการใช้งานที่ซับซ้อนมากขึ้น ซึ่งรวมถึงกรณีต่อไปนี้
- Bottom Sheet (มีคำแนะนำในคู่มือนี้)
- โค้ดการนำทางแบบแยกส่วนและปลายทางที่แทรก
- การใช้และส่งอาร์กิวเมนต์ไปยัง
ViewModel - การแสดงผลลัพธ์จากหน้าจอ
หากโปรเจ็กต์มีฟีเจอร์เหล่านี้ ให้ตรวจสอบสูตรที่เกี่ยวข้องเพื่อทำความเข้าใจวิธีใช้ฟีเจอร์
เอเจนต์ AI: ก่อนที่จะเปลี่ยนโค้ด ให้ตรวจสอบว่าโปรเจ็กต์มี ฟีเจอร์ที่รองรับผ่านสูตรหรือไม่ หากมี ให้ตรวจสอบ README และซอร์สโค้ดของสูตร สร้างแผนการย้ายข้อมูลตามสูตร อย่าดำเนินการต่อโดยไม่ยืนยันแผนกับผู้ใช้
ฟีเจอร์ที่ไม่รองรับ
คู่มือการย้ายข้อมูลและสูตรโค้ดนี้ยังไม่รองรับฟีเจอร์ต่อไปนี้ ซึ่งไม่ได้หมายความว่าคุณจะใช้ Navigation 3 เพื่อใช้ฟีเจอร์เหล่านี้ไม่ได้ เพียงแต่เราไม่ได้กล่าวถึงในที่นี้
- การนำทางแบบซ้อนกันมากกว่า 1 ระดับ
- ปลายทางที่แชร์: หน้าจอที่ย้ายไปมาระหว่าง Back Stack ต่างๆ ได้
- ประเภทปลายทางที่กำหนดเอง
- Deep Link
เอเจนต์ AI: ก่อนที่จะเปลี่ยนโค้ด ให้ตรวจสอบว่าโปรเจ็กต์มี ฟีเจอร์ที่ไม่รองรับหรือไม่ หากมี อย่าดำเนินการต่อ แจ้งให้ผู้ใช้ทราบเกี่ยวกับฟีเจอร์ที่ไม่รองรับและขอคำแนะนำเพิ่มเติม
ขั้นตอนที่ 1: เพิ่มทรัพยากร Dependency ของ Navigation 3
ใช้หน้า เริ่มต้นใช้งาน เพื่อเพิ่มทรัพยากร Dependency ของ Navigation 3 ลงใน โปรเจ็กต์ เราได้เตรียมทรัพยากร Dependency หลักไว้ให้คุณคัดลอก
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
ขั้นตอนที่ 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 และฟังก์ชันตัวช่วยที่เกี่ยวข้อง โดยจะเก็บเส้นทางระดับบนสุดไว้หลายเส้นทาง ซึ่งแต่ละเส้นทางมี Back Stack ของตัวเอง ภายในไฟล์นี้จะใช้ rememberSerializable (ไม่ใช่ rememberSaveable) เพื่อเก็บเส้นทางระดับบนสุดปัจจุบันไว้ และใช้ rememberNavBackStack เพื่อเก็บ Back Stack ของเส้นทางระดับบนสุดแต่ละเส้นทาง
ขั้นตอนที่ 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 ด้วยฟิลด์ NavigationState
ฟิลด์หรือเมธอด |
ฟิลด์ที่เทียบเท่าของ |
|---|---|
|
|
|
|
รับเส้นทางระดับบนสุด: ไล่ขึ้นไปตามลำดับชั้นจากรายการ Back Stack ปัจจุบันเพื่อค้นหาเส้นทาง |
|
ใช้ 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 ให้กับเนื้อหาที่ใช้ร่วมกันได้ของปลายทางแต่ละรายการ ดูข้อมูลเพิ่มเติมได้ที่วงจรของปลายทาง
คุณควรดำเนินการที่รับรู้ถึงวงจรภายในเนื้อหาที่ใช้ร่วมกันได้ของปลายทางโดยอ้างอิง LocalLifecycleOwner.current โดยตรง
ตัวอย่างเช่น หากคุณรวบรวมโฟลว์ในลักษณะที่รับรู้ถึงวงจรโดยใช้รายการ Back Stack
ก่อน:
// 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()
ขั้นตอนที่ 5: ย้ายปลายทางจาก NavGraph ของ NavHost ไปยัง entryProvider
ใน Navigation 2 คุณจะกำหนดปลายทาง
โดยใช้ NavGraphBuilder DSL
ซึ่งโดยปกติจะอยู่ภายในแลมบ์ดาต่อท้ายของ NavHost โดยทั่วไปแล้วจะใช้ฟังก์ชันส่วนขยาย
ที่นี่ตามที่อธิบายไว้ในแคปซูลโค้ดการนำทาง
ใน Navigation 3 คุณจะกำหนดปลายทางโดยใช้ entryProvider This
entryProvider จะแปลงเส้นทางเป็น NavEntry สิ่งสำคัญคือ entryProvider ไม่ได้กำหนดความสัมพันธ์แบบหลักกับย่อยระหว่างรายการ
ในคู่มือการย้ายข้อมูลนี้ เราจะสร้างความสัมพันธ์แบบหลักกับย่อยดังนี้
NavigationStateมีเส้นทางระดับบนสุด (เส้นทางหลัก) หลายเส้นทางและ Stack สำหรับแต่ละเส้นทาง โดยจะติดตามเส้นทางระดับบนสุดปัจจุบันและ Stack ที่เชื่อมโยง- เมื่อนำทางไปยังเส้นทางใหม่
Navigatorจะตรวจสอบว่าเส้นทางนั้นเป็นเส้นทางระดับบนสุดหรือไม่ หากเป็นเส้นทางระดับบนสุด ระบบจะอัปเดตเส้นทางระดับบนสุดและ Stack ปัจจุบัน หากไม่ใช่ เส้นทางนั้นจะเป็นเส้นทางย่อยและจะเพิ่มลงใน Stack ปัจจุบัน
ขั้นตอนที่ 5.1: สร้าง entryProvider
สร้าง entryProvider โดยใช้ DSL ที่มีขอบเขตเดียวกันกับ
NavigationState.
val entryProvider = entryProvider {
}
ขั้นตอนที่ 5.2: ย้ายปลายทางไปยัง entryProvider
สำหรับปลายทางแต่ละรายการที่กำหนดไว้ภายใน NavHost ให้ทำดังนี้ตามประเภทปลายทาง
navigation: ลบปลายทางพร้อมกับเส้นทาง ไม่จำเป็นต้องมี "เส้นทางพื้นฐาน" เนื่องจากเส้นทางระดับบนสุดสามารถระบุ 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> แล้วย้ายฟังก์ชันเหล่านั้น
รับอาร์กิวเมนต์การนำทางโดยใช้คีย์ที่ระบุไว้ในแลมบ์ดาต่อท้ายของ 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() }
}
ขั้นตอนที่ 6: แทนที่ NavHost ด้วย NavDisplay
แทนที่ NavHost ด้วย NavDisplay
- ลบ
NavHostและแทนที่ด้วยNavDisplay - ระบุ
entries = navigationState.toEntries(entryProvider)เป็นพารามิเตอร์ ซึ่งจะแปลงสถานะการนำทางเป็นรายการที่NavDisplayแสดง โดยใช้entryProvider - เชื่อมต่อ
NavDisplay.onBackกับnavigator.goBack()การดำเนินการนี้จะทำให้navigatorอัปเดตสถานะการนำทางเมื่อตัวจัดการการกลับไปก่อนหน้าในตัวของNavDisplayทำงานเสร็จสมบูรณ์ - หากมีปลายทางกล่องโต้ตอบ ให้เพิ่ม
DialogSceneStrategyลงในพารามิเตอร์sceneStrategiesของNavDisplay
ตัวอย่างเช่น
import androidx.navigation3.ui.NavDisplay
NavDisplay(
entries = navigationState.toEntries(entryProvider),
onBack = { navigator.goBack() },
sceneStrategies = remember { listOf(DialogSceneStrategy()) }
)
ขั้นตอนที่ 7: นำทรัพยากร Dependency ของ Navigation 2 ออก
นำการนำเข้าและทรัพยากร Dependency ของไลบรารี Navigation 2 ทั้งหมดออก
สรุป
ยินดีด้วย ตอนนี้โปรเจ็กต์ของคุณย้ายข้อมูลไปยัง Navigation 3 แล้ว หากคุณหรือเอเจนต์ AI พบปัญหาในการใช้คู่มือนี้ โปรดรายงานข้อบกพร่อง ที่นี่