Memigrasikan Jetpack Navigation ke Navigation Compose

Navigation Compose API memungkinkan Anda bernavigasi di antara composable dalam aplikasi Compose, sekaligus memanfaatkan komponen, infrastruktur, dan fitur Jetpack Navigation.

Halaman ini menjelaskan cara bermigrasi dari Navigasi Jetpack berbasis Fragment ke Navigation Compose, sebagai bagian dari migrasi UI berbasis View yang lebih besar ke Jetpack Compose.

Prasyarat migrasi

Anda dapat bermigrasi ke Navigation Compose setelah dapat mengganti semua Fragment dengan composable layar yang sesuai. Composable layar dapat berisi gabungan konten Compose dan View, tetapi semua tujuan navigasi harus composable untuk mengaktifkan migrasi Compose Navigasi. Sampai saat itu, Anda harus terus menggunakan komponen Navigasi berbasis Fragment di codebase View dan Compose interop. Lihat dokumentasi interop navigasi untuk mengetahui informasi selengkapnya.

Menggunakan Navigation Compose di aplikasi khusus Compose bukanlah prasyarat. Anda dapat terus menggunakan komponen Navigasi berbasis Fragmen, selama Anda mempertahankan Fragmen untuk menghosting konten composable.

Langkah migrasi

Baik Anda mengikuti strategi migrasi yang direkomendasikan atau mengambil pendekatan lain, Anda akan mencapai titik ketika semua tujuan navigasi adalah composable layar, dengan Fragment hanya bertindak sebagai penampung composable. Pada tahap ini, Anda dapat bermigrasi ke Navigation Compose.

Jika aplikasi Anda sudah mengikuti pola desain UDF dan panduan untuk arsitektur, migrasi ke Jetpack Compose dan Navigation Compose tidak seharusnya memerlukan pemfaktoran ulang besar pada lapisan lain aplikasi Anda, selain lapisan UI.

Untuk bermigrasi ke Navigation Compose, ikuti langkah-langkah berikut:

  1. Tambahkan dependensi Navigation Compose ke aplikasi Anda.
  2. Buat composable App-level dan tambahkan ke Activity sebagai titik entri Compose, yang menggantikan penyiapan tata letak View:

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

  3. Buat jenis untuk setiap tujuan navigasi. Gunakan data object untuk tujuan yang tidak memerlukan data apa pun dan data class atau class untuk tujuan yang memerlukan data.

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

  4. Siapkan NavController di tempat semua composable yang perlu mereferensikannya dapat mengaksesnya (biasanya di dalam composable App Anda). Pendekatan ini mengikuti prinsip pengangkatan status dan memungkinkan Anda menggunakan NavController sebagai sumber tepercaya untuk menavigasi di antara layar composable dan mempertahankan data sebelumnya:

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

  5. Buat NavHost aplikasi Anda di dalam composable App dan teruskan navController:

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

  6. Tambahkan tujuan composable untuk mem-build grafik navigasi Anda. Jika setiap layar sebelumnya telah dimigrasikan ke Compose, langkah ini hanya terdiri dari mengekstrak composable layar ini dari Fragment Anda ke tujuan 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. Jika Anda mengikuti panduan tentang merencanakan UI Compose, terutama cara ViewModel dan peristiwa navigasi harus diteruskan ke composable, langkah berikutnya adalah mengubah cara Anda menyediakan ViewModel ke setiap composable layar. Anda sering dapat menggunakan injeksi Hilt dan titik integrasinya dengan Compose dan Navigation melalui hiltViewModel:

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

  8. Ganti semua panggilan navigasi findNavController() dengan panggilan navController dan teruskan sebagai peristiwa navigasi ke setiap layar composable, bukan meneruskan seluruh navController. Pendekatan ini mengikuti praktik terbaik untuk mengekspos peristiwa dari fungsi composable kepada pemanggil dan mempertahankan navController sebagai satu sumber tepercaya.

    Data dapat diteruskan ke tujuan dengan membuat instance class rute yang ditentukan untuk tujuan tersebut. Kemudian, data ini dapat diperoleh secara langsung dari entri data sebelumnya di tujuan atau dari ViewModel menggunakan 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. Hapus semua Fragment, tata letak XML yang relevan, navigasi yang tidak diperlukan, dan resource lainnya, serta dependensi Fragment dan Jetpack Navigation yang sudah tidak berlaku.

Anda dapat menemukan langkah-langkah yang sama dengan detail terkait Navigation Compose lainnya di Dokumentasi penyiapan.

Kasus penggunaan umum

Apa pun komponen Navigation yang Anda gunakan, prinsip navigasi yang sama berlaku.

Kasus penggunaan umum saat melakukan migrasi meliputi:

Untuk informasi selengkapnya tentang kasus penggunaan ini, lihat Menavigasi dengan Compose.

Mengambil data kompleks saat menavigasi

Sebaiknya jangan teruskan objek data yang kompleks saat menavigasi. Sebagai gantinya, teruskan informasi minimum yang diperlukan, seperti ID unik atau bentuk ID lainnya, sebagai argumen saat melakukan tindakan navigasi. Anda harus menyimpan objek kompleks sebagai data dalam satu sumber tepercaya, seperti lapisan data. Untuk informasi selengkapnya, lihat Mengambil data kompleks saat menavigasi.

Jika Fragmen Anda meneruskan objek kompleks sebagai argumen, pertimbangkan untuk memfaktorkan ulang kode Anda terlebih dahulu, dengan cara yang memungkinkan penyimpanan dan pengambilan objek ini dari lapisan data. Lihat repositori Now in Android untuk contoh.

Batasan

Bagian ini menjelaskan batasan saat ini untuk Navigation Compose.

Migrasi inkremental ke Navigation Compose

Saat ini, Anda tidak dapat menggunakan Navigation Compose saat masih menggunakan Fragment sebagai tujuan dalam kode Anda. Untuk mulai menggunakan Navigation Compose, semua tujuan Anda harus berupa composable. Anda dapat melacak permintaan fitur ini di Issue Tracker.

Animasi transisi

Mulai dari Navigation 2.7.0-alpha01, dukungan untuk menetapkan transisi kustom, yang sebelumnya berasal dari AnimatedNavHost, kini didukung secara langsung di NavHost. Baca catatan rilis untuk mengetahui informasi selengkapnya.

Pelajari lebih lanjut

Untuk informasi selengkapnya tentang cara bermigrasi ke Navigation Compose, lihat referensi berikut:

  • Codelab Navigation Compose: Pelajari dasar-dasar Navigation Compose dengan codelab praktis.
  • Repositori Now in Android: Aplikasi Android yang berfungsi penuh dan sepenuhnya di-build dengan Kotlin dan Jetpack Compose, yang mengikuti praktik terbaik desain dan pengembangan Android serta menyertakan Navigation Compose.
  • Migrasi Sunflower ke Jetpack Compose: Postingan blog yang mendokumentasikan perjalanan migrasi aplikasi contoh Sunflower dari View ke Compose, yang juga mencakup migrasi ke Navigation Compose.
  • Jetnews untuk semua layar: Postingan blog yang mendokumentasikan pemfaktoran ulang dan migrasi contoh Jetnews untuk mendukung semua layar dengan Jetpack Compose dan Navigation Compose.