Navigation 3 thể hiện một sự thay đổi cơ bản trong cách Jetpack Compose xử lý trạng thái điều hướng và mang lại những lợi thế đáng kể về kiến trúc so với Navigation 2.
Tìm hiểu về những thay đổi về cấu trúc và các bước cần thiết để di chuyển một ứng dụng Wear Compose từ Navigation 2 sang Navigation 3.
Các lợi thế chính của Navigation 3
- Kiểm soát trực tiếp ngăn xếp lui: Về cơ bản,
NavBackStackchỉ là một danh sách có thể thay đổi gồm các đối tượngNavKey, đại diện cho nhật ký màn hình mà người dùng đã truy cập. Bạn kiểm soát nó giống hệt như cách bạn kiểm soát bất kỳMutableListKotlin nào (add,removeLast,clear). Bạn trực tiếp thao tác với danh sách để thực hiện các thao tác điều hướng, chẳng hạn như thêm một khoá để chuyển tiếp hoặc xoá một khoá để quay lại. - Thiết kế ưu tiên Compose: Ngăn xếp lui được mô hình hoá dưới dạng trạng thái có thể quan sát tiêu chuẩn. Việc sửa đổi lịch sử điều hướng của bạn cũng giống như việc cập nhật mọi state Compose khác, tự động kích hoạt quá trình recomposition để hiển thị màn hình hiện tại.
- An toàn về kiểu theo mặc định: Các tuyến đường dựa trên chuỗi sẽ bị loại bỏ hoàn toàn. Chỉ đường sử dụng các đối tượng dữ liệu và lớp dữ liệu có thể chuyển đổi tuần tự.
- Trình bày tách biệt (Chiến lược cảnh): Lớp chuyển đổi giao diện người dùng (
NavDisplayvàSwipeDismissableSceneStrategy) hoàn toàn tách biệt với tính năng theo dõi trạng thái (NavBackStack), giúp đơn giản hoá việc tích hợp các hiệu ứng chuyển đổi điều hướng tích hợp sẵn của Wear OS.
Các bước di chuyển
1. Cập nhật phần phụ thuộc
Xoá phần phụ thuộc androidx.wear.compose:compose-navigation cũ và giới thiệu các phần phụ thuộc Navigation 3 mới được phân chia, cùng với khả năng hỗ trợ việc chuyển đổi tuần tự Kotlin.
Xoá:
implementation("androidx.wear.compose:compose-navigation:...")
Thêm:
implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...") // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin
2. Cập nhật đích đến để triển khai NavKey
Trong Navigation 2, bạn có thể đã sử dụng các chuỗi hoặc đối tượng chung để định tuyến. Trong Điều hướng 3, bạn phải triển khai giao diện điểm đánh dấu NavKey và chú thích mọi đối tượng màn hình bằng @Serializable.
Tại sao yêu cầu điều này? Để đảm bảo rằng ngăn xếp lui có thể được lưu và khôi phục trong quá trình bị buộc tắt, navigation3-runtime cơ bản sẽ dựa vào kotlinx-serialization để chuyển đổi trạng thái thành chuỗi.
Trước (Navigation 2 – Tuyến đường chung an toàn về kiểu):
sealed class Nav2Screen { data object Landing : Nav2Screen() data object List : Nav2Screen() }
Sau (Điều hướng 3 – NavKey + Serializable):
@Serializable sealed interface MigrationScreen : NavKey { @Serializable data object Landing : MigrationScreen @Serializable data object List : MigrationScreen }
3. Thay thế Logic định tuyến (NavController thành NavBackStack)
Thay thế NavController bằng NavBackStack được khởi tạo thông qua rememberNavBackStack. Bạn cũng cần tạo thực thể SwipeDismissableSceneStrategy dành riêng cho Wear OS.
Trước (Navigation 2):
val navController = rememberSwipeDismissableNavController()
Sau (Điều hướng 3):
val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey) val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()
4. Thay thế NavHost bằng NavDisplay và DSL entryProvider
Vùng chứa NavHost và DSL trình tạo composable("route") { ... } nội bộ của vùng chứa này được thay thế bằng NavDisplay và DSL entryProvider {
entry<Key> { ... } }.
Trước (Navigation 2):
SwipeDismissableNavHost(navController = navController, startDestination = "menu") { composable("menu") { GreetingScreen( onShowList = { navController.navigate("list") } ) } composable("list") { ListScreen() } }
Sau (Điều hướng 3):
NavDisplay( backStack = backStack, sceneStrategies = listOf(strategy), entryProvider = entryProvider { entry<MigrationScreen.Landing> { GreetingScreen( onShowList = { backStack.add(MigrationScreen.List) } ) } entry<MigrationScreen.List> { ListScreen() } } )