Navigation Compose เป็นไลบรารี Jetpack สำหรับการนำทางแบบประกาศภายในแอปพลิเคชัน Android ที่สร้างบน Jetpack Compose แทนที่จะใช้ FragmentManager หรือการนำทางแบบ Intent Navigation Compose นำเสนอกราฟเส้นทางเดียวที่จัดการผ่าน NavController และ NavHost ตามข้อมูลจาก Google I/O (2025) Navigation Compose เป็นวิธีการนำทางที่แนะนำสำหรับแอปพลิเคชัน Compose ซึ่งใช้ในมากกว่า 70% ของโปรเจกต์ใหม่ ไลบรารีรองรับการส่งอาร์กิวเมนต์แบบกำหนดชนิด ลิงก์ลึก แอนิเมชันการเปลี่ยนภาพ และการรวมเข้ากับ ViewModel ผ่าน SavedStateHandle
ประเด็นสำคัญ
Navigation Compose เป็นไลบรารีในชุด Jetpack ที่ให้เฟรมเวิร์กการนำทางสำหรับแอปพลิเคชัน Compose ไลบรารีนี้ใช้หลักการเดียวกันกับ Navigation Component สำหรับระบบ View แต่ปรับให้เข้ากับธรรมชาติแบบประกาศของ Compose: แทนที่จะใช้ FragmentTransaction จะใช้ฟังก์ชัน composable และกราฟการนำทางถูกสร้างผ่าน Kotlin DSL
ความแตกต่างหลักระหว่าง Navigation Compose กับการนำทางแบบดั้งเดิมคือการไม่มี FragmentManager แต่ละหน้าจอเป็นฟังก์ชัน composable ที่เรนเดอร์ใน NavHost เมื่อเส้นทางตรงกัน สแต็กย้อนกลับจัดเก็บไม่ใช่ Fragment แต่เป็นบันทึกที่มีเส้นทาง อาร์กิวเมนต์ และสถานะ ซึ่งทำให้สถาปัตยกรรมง่ายขึ้นและขจัดความขัดแย้งของวงจรชีวิตทั่วไปของการนำทางแบบ Fragment
ตามข้อมูลจาก Google (2025) Navigation Compose ได้เปลี่ยนจากขั้นทดลองเป็นเสถียร และเป็นส่วนหนึ่งของ Jetpack ตั้งแต่เวอร์ชัน 2.8.0 ไลบรารีรองรับ Material3, การนำทางแบบชนิดปลอดภัย (ผ่าน Kotlin Serialization), กราฟที่ซ้อนกัน และการทำให้เป็นโมดูล ข้อจำกัดเดียวคือไลบรารีไม่รองรับสแต็กย้อนกลับหลายรายการสำหรับ BottomNavigation โดยไม่มีการกำหนดค่าด้วยตนเอง แม้ว่า Google จะกำลังดำเนินการแก้ไข
สถาปัตยกรรมของ Navigation Compose หมุนรอบสามเอนทิตี: NavController (การจัดการสแต็ก), NavHost (คอนเทนเนอร์กราฟ) และ NavDestination (เส้นทางเดี่ยวพร้อม composable) การโต้ตอบระหว่างกันเป็นแบบประกาศ: นักพัฒนาอธิบายเส้นทางและอาร์กิวเมนต์ และไลบรารีจัดการสถานะการโหลด การเก็บรักษา และการกู้คืน
NavController เป็นองค์ประกอบกลางของ Navigation Compose ที่จัดการสแต็กการนำทาง มันถูกสร้างผ่าน rememberNavController() และส่งต่อไปยัง NavHost NavController จัดเก็บสแต็กย้อนกลับ จุดเริ่มต้นปัจจุบัน และรองรับการดำเนินการที่เลื่อนออกไป (ดีพลิงก์หลังการเริ่มต้นกราฟ)
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile/{userId}") { backStackEntry ->
ProfileScreen(
userId = backStackEntry.arguments?.getString("userId") ?: ""
)
}
}
}
วิธีการหลักของ NavController: navigate(route) — นำทางไปยังเส้นทาง, popBackStack() — กลับไปยังหน้าจอก่อนหน้า, navigateAndClear(route) — นำทางและล้างสแต็ก NavOptions ระบุพฤติกรรม: launchSingleTop ป้องกันการซ้ำ, popUpTo ล้างสแต็กจนถึงเส้นทางที่ระบุ, restoreState กู้คืนสถานะก่อนหน้า
เพื่อเข้าถึง NavController จากฟังก์ชัน composable ที่ซ้อนกันลึก ให้ใช้ NavHostController ผ่าน CompositionLocal LocalNavController มีให้ใน ScopedNavController ภายใน NavHost ภายนอก NavHost (เช่น ใน BottomNavigation) ตัวควบคุมถูกส่งผ่านพารามิเตอร์หรือ ViewModel
NavHost เป็นคอนเทนเนอร์ composable ที่เชื่อมต่อ NavController กับกราฟเส้นทาง แต่ละเส้นทางถูกประกาศผ่าน composable(route, arguments, deepLinks) โดยที่ route คือสตริงเส้นทางที่มีตัวยึดตำแหน่ง {param} ไม่บังคับ เมื่อเส้นทางปัจจุบันตรงกัน NavHost จะเรนเดอร์บล็อก composable ที่เกี่ยวข้อง
กราฟเส้นทางถูกสร้างแบบลำดับชั้น: กราฟสามารถซ้อนกันผ่าน navigation() เพื่อจัดกลุ่มเส้นทางภายในโมดูล กราฟที่ซ้อนกันมี startDestination ของตัวเองและรวมกันภายใต้คำนำหน้าเส้นทางร่วม ซึ่งช่วยให้จัดระเบียบสถาปัตยกรรมแบบโมดูลที่แต่ละโมดูลคุณสมบัติลงทะเบียนกราฟย่อยของตัวเอง
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
navigation(
route = "auth",
startDestination = "auth/login"
) {
composable("auth/login") { LoginScreen(navController) }
composable("auth/register") { RegisterScreen(navController) }
}
}
NavHost จัดการปุ่มย้อนกลับของระบบ (back press) โดยอัตโนมัติผ่าน LocalBackDispatcher ใน Material3 Scaffold โดยค่าเริ่มต้นมันจะรับ NavController เพื่อการทำงานที่ถูกต้องของ BottomNavigation NavHost สร้าง composable ใหม่เมื่อเส้นทางเปลี่ยนแปลง แต่คงสถานะผ่าน rememberSaveable สำหรับฟิลด์ป้อนข้อมูลและการเลื่อน
Navigation Compose รองรับการส่ง อาร์กิวเมนต์แบบกำหนดชนิด ระหว่างหน้าจอผ่านพารามิเตอร์เส้นทางและ NavType พารามิเตอร์ถูกระบุในเส้นทางเป็น {paramName} โดยระบุชนิดผ่าน arguments ใน composable() NavType รองรับ String, Int, Long, Float, Boolean, Parcelable และ Serializable
| ชนิดอาร์กิวเมนต์ | NavType | ตัวอย่างเส้นทาง |
|---|---|---|
| String | NavType.StringType | "profile/{name}" |
| Int | NavType.IntType | "item/{id}" |
| Boolean | NavType.BoolType | "settings?enabled={flag}" |
| Parcelable | NavType.ParcelableType | "details/{item}" |
| Float | NavType.FloatType | "map?lat={lat}&lng={lng}" |
อาร์กิวเมนต์ถูกแยกจาก NavBackStackEntry ผ่าน arguments?.getType(key) สำหรับพารามิเตอร์บังคับให้ใช้ defaultValue สำหรับพารามิเตอร์ไม่บังคับ — nullable การรองรับ Parcelable ทำงานเฉพาะกับ Kotlin Parcelize หรือไลบรารี kotlinx.parcelize สำหรับออบเจกต์ที่ซับซ้อน แนะนำให้ส่ง ID และโหลดข้อมูลผ่าน ViewModel แทนที่จะทำให้ออบเจกต์ทั้งหมดเป็นอนุกรม
ตั้งแต่ Navigation 2.8.0 มีการนำ การนำทางแบบชนิดปลอดภัย ด้วย Kotlin Serialization: เส้นทางถูกกำหนดเป็นคลาสข้อมูลและอาร์กิวเมนต์เป็นฟิลด์ ซึ่งแทนที่สตริงเส้นทางด้วยออบเจกต์แบบกำหนดชนิดและกำจัดข้อผิดพลาดในชื่อเส้นทาง การย้ายข้อมูลจำเป็นต้องใช้ปลั๊กอิน Kotlin Serialization และการพึ่งพา navigation-compose-typesafe
@Serializable
/* sealed class Route */
sealed class ProfileRoute(val route: String) {
data object Home : ProfileRoute("home")
data class Profile(val userId: String) : ProfileRoute("profile/{userId}")
}
ลิงก์ลึก เป็นกลไกการนำทางที่ช่วยให้เปิดหน้าจอแอปพลิเคชันเฉพาะผ่าน URL หรือ intent-filter ใน Navigation Compose ลิงก์ลึกถูกกำหนดค่าผ่านพารามิเตอร์ deepLinks ใน composable() และถูกประมวลผลโดยอัตโนมัติเมื่อ URI ตรงกับรูปแบบ
ลิงก์ลึกถูกระบุเป็นรายการ UriPattern: "https://example.com/profile/{userId}" พารามิเตอร์ URI จะถูกแมปไปยังอาร์กิวเมนต์เส้นทางโดยอัตโนมัติ NavController ประมวลผลลิงก์ลึกเมื่อเริ่มต้นแอปพลิเคชัน (ผ่าน intent) และระหว่างการทำงาน (ผ่านลิงก์ลึกโดยนัย) เพื่อจัดการลิงก์ลึกที่รอการดำเนินการ ให้ใช้ handleDeepLink() ใน NavController หลังจากการเริ่มต้นกราฟ
ตามข้อมูลของ Google ลิงก์ลึกแนะนำสำหรับ: การแจ้งเตือนแบบพุช (Firebase Dynamic Links), การยืนยันอีเมล, การแชร์เนื้อหา และการนำทางข้ามจากลิงก์เว็บ สำหรับ Android 12+ ใช้ Digital Asset Links เพื่อตรวจสอบสิทธิ์ของลิงก์ลึก AndroidManifest.xml ต้องมี intent-filter ที่มี autoVerify="true" เพื่อเปิดลิงก์โดยไม่ต้องแสดงไดอะล็อก
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "https://example.com/profile/{userId}" }
)
) { backStackEntry ->
ProfileScreen(userId = backStackEntry.arguments?.getString("userId") ?: "")
}
ข้อจำกัดของลิงก์ลึกใน Navigation Compose: ไลบรารีไม่รองรับ ลิงก์ลึกที่เลื่อนออกไป — ลิงก์ลึกจะถูกประมวลผลหลังจาก NavHost สร้างกราฟเสร็จสมบูรณ์แล้วเท่านั้น หากลิงก์ลึกมาถึงก่อนการเริ่มต้นกราฟ จะต้องเลื่อนออกไปผ่าน intent?.data และประมวลผลใน LaunchedEffect สำหรับ Firebase Dynamic Links ให้ใช้ Firebase Dynamic Links SDK ร่วมกับ Navigation Compose
Navigation Compose รองรับแอนิเมชันการเปลี่ยนภาพผ่านพารามิเตอร์ enterTransition, exitTransition, popEnterTransition และ popExitTransition ใน composable() แอนิเมชันถูกใช้งานโดยใช้ Compose Animation API: fadeIn, slideInHorizontally, expandIn และอื่น ๆ โดยค่าเริ่มต้น แอนิเมชันถูกปิดใช้งาน — หน้าจอถูกเปลี่ยนทันที
สถานการณ์แอนิเมชันทั่วไป: slideInHorizontally สำหรับการนำทางไปข้างหน้า (หน้าจอเลื่อนเข้ามาจากทางขวา), slideOutHorizontally สำหรับการกลับ (หน้าจอเลื่อนออกไปทางขวา) สำหรับ BottomNavigation แอนิเมชันแบบค่อยๆ จางโดยไม่มีการเลื่อนถูกใช้ทั่วไปมากกว่า แอนิเมชันถูกตั้งค่าผ่าน NavHost และใช้กับ composable ทั้งหมดเว้นแต่จะระบุแอนิเมชันเฉพาะ
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally() + fadeIn() },
exitTransition = { slideOutHorizontally() + fadeOut() },
popEnterTransition = { fadeIn() },
popExitTransition = { slideOutHorizontally() + fadeOut() }
) { /* composable */ }
แอนิเมชันสามารถแทนที่สำหรับแต่ละ composable เป็นรายบุคคลโดยส่งพารามิเตอร์แอนิเมชันโดยตรงใน composable() สิ่งสำคัญคือแอนิเมชันต้องไม่ขัดแย้งกับแอนิเมชันการกดย้อนกลับของระบบ สำหรับการเปลี่ยนภาพองค์ประกอบที่ใช้ร่วมกัน จำเป็นต้องใช้ไลบรารี accompanist-navigation-animation หรือการใช้งานแบบกำหนดเองผ่าน Modifier.graphicsLayer ตามข้อมูลจาก Android Developers (2025) 80% ของแอปพลิเคชันในproduction ใช้แอนิเมชันเลื่อนแนวนอนสำหรับการนำทางมาตรฐาน
คำถามที่พบบ่อย
Navigation Compose ทำงานโดยไม่ต้องใช้ Fragment ใช้ฟังก์ชัน composable และ Kotlin DSL สำหรับกราฟ Navigation Component (View) ใช้ FragmentManager และกราฟ XML เวอร์ชัน Compose ง่ายกว่า เร็วกว่า และไม่มีวงจรชีวิตของ Fragment Navigation Component สำหรับ View เหมาะสำหรับแอปพลิเคชันแบบไฮบริดเท่านั้น
แนะนำให้ส่ง ID ของออบเจกต์และโหลดข้อมูลผ่าน ViewModel ด้วย SavedStateHandle หากออบเจกต์ง่าย ให้ใช้ Parcelable ผ่าน kotlinx.parcelize การส่งออบเจกต์ขนาดใหญ่โดยตรงผ่านอาร์กิวเมนต์ (Bundle) ถูกจำกัดที่ ~1 MB และอาจทำให้เกิด TransactionTooLargeException
ใช่ ผ่านฟังก์ชัน navigation(route, startDestination) ภายใน NavHost กราฟที่ซ้อนกันมี startDestination ของตัวเองและรวมกันภายใต้คำนำหน้าเส้นทางร่วม ซึ่งช่วยให้จัดระเบียบสถาปัตยกรรมแบบโมดูลด้วยกราฟที่แยกสำหรับแต่ละโมดูลคุณสมบัติ
NavController จัดการการกดย้อนกลับโดยอัตโนมัติผ่าน BackHandler จาก Compose เรียก navController.popBackStack() เมื่อกดย้อนกลับ สำหรับการจัดการแบบกำหนดเอง (การยืนยันการออก) ให้ใช้ BackHandler(enabled = condition) { callback } ก่อนเรียก popBackStack()
ปัจจุบัน Navigation Compose ไม่รองรับ ใน Compose Multiplatform สำหรับส่วน iOS ของโปรเจกต์ข้ามแพลตฟอร์ม ให้ใช้ Voyager หรือ Decompose Google กำลังทำงานรองรับ KMP แต่ไม่มีกำหนดการเผยแพร่ สำหรับโปรเจกต์ที่ใช้ Android เท่านั้น Navigation Compose เป็นตัวเลือกเดียวที่แนะนำ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม