Di chuyển Jetpack Navigation sang Navigation Compose

API Điều hướng Compose cho phép bạn điều hướng giữa các thành phần kết hợp trong ứng dụng Compose, đồng thời tận dụng thành phần, cơ sở hạ tầng và các tính năng của Điều hướng Jetpack.

Trang này mô tả cách di chuyển từ Jetpack Navigation dựa trên Mảnh sang Navigation Compose, trong quá trình di chuyển giao diện người dùng lớn hơn dựa trên Khung hiển thị sang Jetpack Compose.

Điều kiện tiên quyết để di chuyển

Bạn có thể chuyển sang Navigation Compose (Thành phần điều hướng Compose) sau khi có thể thay thế tất cả các Mảnh bằng các thành phần kết hợp màn hình tương ứng. Thành phần kết hợp màn hình có thể chứa nội dung kết hợp của Compose và Chế độ xem, nhưng tất cả đích đến điều hướng phải là thành phần kết hợp để cho phép di chuyển Điều hướng Compose. Cho đến lúc đó, bạn nên tiếp tục sử dụng Thành phần điều hướng dựa trên mảnh trong cơ sở mã Khung hiển thị và Compose tương tác. Hãy xem tài liệu về khả năng tương tác của tính năng điều hướng để biết thêm thông tin.

Bạn không bắt buộc phải sử dụng Navigation Compose trong ứng dụng chỉ có Compose. Bạn có thể tiếp tục sử dụng Thành phần điều hướng dựa trên mảnh, miễn là bạn vẫn giữ Mảnh để lưu trữ nội dung có thể kết hợp.

Các bước di chuyển

Cho dù bạn đang làm theo chiến lược di chuyển được đề xuất của chúng tôi hay sử dụng một phương pháp khác, bạn sẽ đạt đến điểm mà tất cả đích đến điều hướng đều là thành phần kết hợp màn hình, trong đó các Mảnh chỉ đóng vai trò là vùng chứa có khả năng kết hợp. Ở bước này, bạn có thể di chuyển sang Navigation Compose.

Nếu ứng dụng của bạn đang tuân theo mẫu thiết kế UDFhướng dẫn về cấu trúc của chúng tôi, thì việc di chuyển sang Jetpack Compose và Navigation Compose sẽ không yêu cầu tái cấu trúc lớn các lớp khác của ứng dụng, ngoài lớp giao diện người dùng.

Để di chuyển sang Navigation Compose, hãy làm theo các bước sau:

  1. Thêm phần phụ thuộc Navigation Compose vào ứng dụng.
  2. Tạo một thành phần kết hợp App-level và thêm thành phần này vào Activity làm điểm truy cập Compose, thay thế chế độ thiết lập bố cục Thành phần hiển thị:

    class SampleActivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // setContentView<ActivitySampleBinding>(this, R.layout.activity_sample)
            setContent {
                SampleApp(/* ... */)
            }
        }
    }

  3. Tạo loại cho từng đích đến điều hướng. Sử dụng data object cho các đích đến không yêu cầu dữ liệu và data class hoặc class cho các đích đến yêu cầu dữ liệu.

    @Serializable data object First
    @Serializable data class Second(val id: String)
    @Serializable data object Third
    

  4. Thiết lập NavController ở vị trí mà tất cả thành phần kết hợp cần tham chiếu đến NavController đều có quyền truy cập (thường là bên trong thành phần kết hợp App). Phương pháp này tuân theo các nguyên tắc chuyển trạng thái lên trên (state hoisting) và cho phép bạn sử dụng NavController làm nguồn đáng tin cậy để di chuyển giữa các màn hình có thể kết hợp và duy trì ngăn xếp lui:

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
        // ...
    }

  5. Tạo NavHost của ứng dụng bên trong thành phần kết hợp App và truyền navController:

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
    
        SampleNavHost(navController = navController)
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            // ...
        }
    }

  6. Thêm các đích đến composable để tạo biểu đồ điều hướng. Nếu mỗi màn hình đã được di chuyển trước đó sang Compose, thì bước này chỉ bao gồm việc trích xuất các thành phần kết hợp màn hình này từ Mảnh vào đích đến composable:

    class FirstFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ComposeView(requireContext()).apply {
                setContent {
                    // FirstScreen(...) EXTRACT FROM HERE
                }
            }
        }
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(/* ... */) // EXTRACT TO HERE
            }
            composable<Second> {
                SecondScreen(/* ... */)
            }
            // ...
        }
    }

  7. Nếu bạn đã làm theo hướng dẫn về cách thiết kế giao diện người dùng Compose, cụ thể là cách truyền ViewModel và sự kiện điều hướng đến các thành phần kết hợp, thì bước tiếp theo là thay đổi cách bạn cung cấp ViewModel cho từng thành phần kết hợp trên màn hình. Bạn thường có thể sử dụng tính năng chèn Hilt và điểm tích hợp của tính năng này với Compose và Navigation thông qua hiltViewModel:

    @Composable
    fun FirstScreen(
        // viewModel: FirstViewModel = viewModel(),
        viewModel: FirstViewModel = hiltViewModel(),
        onButtonClick: () -> Unit = {},
    ) {
        // ...
    }

  8. Thay thế tất cả lệnh gọi điều hướng findNavController() bằng lệnh gọi navController và truyền các lệnh gọi này dưới dạng sự kiện điều hướng đến từng màn hình có thể kết hợp, thay vì truyền toàn bộ navController. Phương pháp này tuân theo các phương pháp hay nhất để hiển thị các sự kiện từ hàm có khả năng kết hợp cho phương thức gọi và giữ navController làm nguồn đáng tin cậy duy nhất.

    Bạn có thể truyền dữ liệu đến một đích đến bằng cách tạo một thực thể của lớp tuyến đường được xác định cho đích đến đó. Sau đó, bạn có thể lấy trực tiếp từ mục ngăn xếp lui tại đích đến hoặc từ ViewModel bằng SavedStateHandle.toRoute().

    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = First) {
            composable<First> {
                FirstScreen(
                    onButtonClick = {
                        // findNavController().navigate(firstScreenToSecondScreenAction)
                        navController.navigate(Second(id = "ABC"))
                    }
                )
            }
            composable<Second> { backStackEntry ->
                val secondRoute = backStackEntry.toRoute<Second>()
                SecondScreen(
                    id = secondRoute.id,
                    onIconClick = {
                        // findNavController().navigate(secondScreenToThirdScreenAction)
                        navController.navigate(Third)
                    }
                )
            }
            // ...
        }
    }

  9. Xoá tất cả các Mảnh, bố cục XML có liên quan, thành phần điều hướng không cần thiết và các tài nguyên khác, cũng như các phần phụ thuộc Mảnh và Điều hướng Jetpack cũ.

Bạn có thể tìm thấy các bước tương tự với nhiều thông tin chi tiết hơn liên quan đến Navigation Compose trong tài liệu Thiết lập.

Các trường hợp sử dụng phổ biến

Bất kể bạn đang sử dụng thành phần Điều hướng nào, các nguyên tắc điều hướng tương tự vẫn áp dụng.

Sau đây là một số trường hợp sử dụng phổ biến khi di chuyển:

Để biết thêm thông tin chi tiết về các trường hợp sử dụng này, hãy xem phần Điều hướng bằng Compose.

Truy xuất dữ liệu phức tạp khi điều hướng

Bạn không nên truyền các đối tượng dữ liệu phức tạp khi điều hướng. Thay vào đó, hãy truyền thông tin tối thiểu cần thiết, chẳng hạn như giá trị nhận dạng duy nhất hoặc hình thức mã nhận dạng khác, làm đối số khi thực hiện các thao tác điều hướng. Bạn nên lưu trữ các đối tượng phức tạp dưới dạng dữ liệu trong một nguồn đáng tin cậy, chẳng hạn như lớp dữ liệu. Để biết thêm thông tin, hãy xem phần Truy xuất dữ liệu phức tạp khi điều hướng.

Nếu các Mảnh của bạn đang truyền các đối tượng phức tạp dưới dạng đối số, trước tiên, hãy cân nhắc việc tái cấu trúc mã của bạn theo cách cho phép lưu trữ và tìm nạp các đối tượng này từ lớp dữ liệu. Hãy xem kho lưu trữ Now in Android để biết ví dụ.

Các điểm hạn chế

Phần này mô tả các giới hạn hiện tại của Navigation Compose.

Di chuyển dần dần sang Navigation Compose

Hiện tại, bạn không thể sử dụng Navigation Compose trong khi vẫn sử dụng Mảnh làm đích đến trong mã. Để bắt đầu sử dụng Navigation Compose, tất cả đích đến của bạn đều cần phải là thành phần kết hợp. Bạn có thể theo dõi yêu cầu về tính năng này trên Công cụ theo dõi lỗi.

Ảnh động chuyển tiếp

Kể từ Navigation 2.7.0-alpha01, tính năng hỗ trợ thiết lập hiệu ứng chuyển đổi tuỳ chỉnh (trước đây là từ AnimatedNavHost) hiện được hỗ trợ trực tiếp trong NavHost. Hãy đọc kỹ ghi chú phát hành để biết thêm thông tin.

Tìm hiểu thêm

Để biết thêm thông tin về cách di chuyển sang Navigation Compose, hãy xem các tài nguyên sau:

  • Lớp học lập trình về Navigation Compose: Tìm hiểu kiến thức cơ bản về Navigation Compose thông qua một lớp học lập trình thực hành.
  • Kho lưu trữ Now in Android: Một ứng dụng Android có đầy đủ chức năng được xây dựng hoàn toàn bằng Kotlin và Jetpack Compose, tuân theo các phương pháp hay nhất về thiết kế và phát triển Android, đồng thời bao gồm cả Navigation Compose.
  • Di chuyển Sunflower sang Jetpack Compose: Bài đăng trên blog ghi lại hành trình di chuyển của ứng dụng mẫu Sunflower từ Khung hiển thị sang Compose, cũng bao gồm cả việc di chuyển sang Navigation Compose.
  • Jetnews cho mọi màn hình: Một bài đăng trên blog ghi lại cách tái cấu trúc và di chuyển mẫu Jetnews để hỗ trợ tất cả màn hình bằng Jetpack Compose và Navigation Compose.