Przenieś nawigację Jetpack do tworzenia wiadomości w Nawigacji

Interfejs Navigation Compose API umożliwia poruszanie się między funkcjami kompozycyjnymi w aplikacji Compose przy jednoczesnym korzystaniu z komponentu, infrastruktury i funkcji Jetpack Navigation.

Na tej stronie opisujemy, jak przeprowadzić migrację z nawigacji Jetpack opartej na fragmentach do nawigacji Compose w ramach większej migracji interfejsu opartego na widokach do Jetpack Compose.

Wymagania wstępne migracji

Możesz przejść na Navigation Compose, gdy zastąpisz wszystkie Fragmenty odpowiednimi komponentami ekranu. Kompozycje ekranu mogą zawierać mieszankę treści Compose i View, ale wszystkie miejsca docelowe nawigacji muszą być kompozycjami, aby umożliwić migrację do Navigation Compose. Do tego czasu w kodzie View i Compose w trybie interop należy nadal używać komponentu nawigacji opartej na fragmentach. Więcej informacji znajdziesz w dokumentacji dotyczącej interoperacyjności nawigacji.

Korzystanie z Navigation Compose w aplikacji opartej wyłącznie na Compose nie jest wymagane. Możesz nadal używać komponentu Nawigacja oparta na fragmentach, o ile będziesz używać fragmentów do hostowania treści, które można komponować.

Etapy migracji

Niezależnie od tego, czy korzystasz z naszej zalecanej strategii migracji, czy stosujesz inne podejście, w pewnym momencie wszystkie miejsca docelowe nawigacji będą komponentami ekranu, a fragmenty będą pełnić rolę tylko kontenerów komponentów. Na tym etapie możesz przejść na Navigation Compose.

Jeśli Twoja aplikacja korzysta już z wzorca projektowego UDF i naszego przewodnika po architekturze, migracja do Jetpack Compose i Navigation Compose nie powinna wymagać większych zmian w innych warstwach aplikacji poza warstwą interfejsu.

Aby przeprowadzić migrację do Navigation Compose, wykonaj te czynności:

  1. Dodaj do aplikacji zależność Navigation Compose.
  2. Utwórz App-level funkcję kompozycyjną i dodaj ją do Activity jako punkt wejścia do Compose, zastępując konfigurację układu View:

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

  3. Utwórz typy dla każdego miejsca docelowego nawigacji. Używaj data object w przypadku miejsc docelowych, które nie wymagają żadnych danych, oraz data class lub class w przypadku miejsc docelowych, które wymagają danych.

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

  4. Skonfiguruj NavController w miejscu, do którego mają dostęp wszystkie funkcje kompozycyjne, które muszą się do niego odwoływać (zwykle jest to w funkcji kompozycyjnej App). To podejście jest zgodne z zasadami przenoszenia stanu i umożliwia używanie NavController jako źródła informacji do poruszania się między ekranami kompozycyjnymi i utrzymywania stosu wstecznego:

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

  5. Utwórz NavHost aplikacji w kompozycyjnym App i przekaż navController:

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

  6. Dodaj miejsca docelowe composable, aby utworzyć wykres nawigacji. Jeśli każdy ekran został wcześniej przeniesiony do Compose, ten krok polega tylko na wyodrębnieniu tych funkcji kompozycyjnych ekranu z fragmentów do miejsc docelowych 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. Jeśli postępujesz zgodnie z wskazówkami dotyczącymi projektowania interfejsu Compose, a zwłaszcza sposobu przekazywania ViewModel i zdarzeń nawigacji do komponentów kompozycyjnych, kolejnym krokiem jest zmiana sposobu dostarczania ViewModel do każdego komponentu kompozycyjnego ekranu. Wstrzykiwania Hilt i jego punktu integracji z Compose i Navigation możesz często używać za pomocą hiltViewModel:

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

  8. Zastąp wszystkie wywołania nawigacji findNavController() wywołaniami navController i przekaż je jako zdarzenia nawigacji do każdego ekranu z funkcją kompozycji, zamiast przekazywać całą wartość navController. To podejście jest zgodne z najlepszymi praktykami udostępniania zdarzeń z funkcji kompozycyjnych wywołującym i utrzymuje navController jako jedyne źródło danych.

    Dane można przekazywać do miejsca docelowego, tworząc instancję klasy trasy zdefiniowanej dla tego miejsca docelowego. Można go następnie uzyskać bezpośrednio z pozycji na liście wstecznej w miejscu docelowym lub z ViewModel za pomocą 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. Usuń wszystkie fragmenty, odpowiednie układy XML, niepotrzebną nawigację i inne zasoby oraz nieaktualne zależności fragmentów i nawigacji Jetpack.

Te same czynności z większą liczbą szczegółów dotyczących Navigation Compose znajdziesz w dokumentacji konfiguracji.

Częste przypadki użycia

Niezależnie od tego, którego komponentu Navigation używasz, obowiązują te same zasady nawigacji.

Typowe przypadki użycia podczas migracji to:

Więcej informacji o tych przypadkach użycia znajdziesz w artykule Nawigacja za pomocą funkcji Compose.

Pobieranie złożonych danych podczas nawigacji

Stanowczo odradzamy przekazywanie złożonych obiektów danych podczas nawigacji. Zamiast tego podczas wykonywania działań związanych z nawigacją przekazuj jako argumenty tylko niezbędne informacje, takie jak unikalny identyfikator lub inny identyfikator. Złożone obiekty należy przechowywać jako dane w jednym źródle informacji, np. w warstwie danych. Więcej informacji znajdziesz w artykule Pobieranie złożonych danych podczas nawigacji.

Jeśli Twoje fragmenty przekazują złożone obiekty jako argumenty, najpierw rozważ refaktoryzację kodu w taki sposób, aby można było przechowywać te obiekty w warstwie danych i pobierać je z niej. Przykłady znajdziesz w repozytorium Now in Android.

Ograniczenia

W tej sekcji opisujemy obecne ograniczenia biblioteki Navigation Compose.

Stopniowa migracja do Navigation Compose

Obecnie nie możesz używać Navigation Compose, jeśli w kodzie nadal używasz fragmentów jako miejsc docelowych. Aby zacząć korzystać z Navigation Compose, wszystkie miejsca docelowe muszą być funkcjami kompozycyjnymi. Możesz śledzić to zgłoszenie funkcji w narzędziu Issue Tracker.

Animacje przejścia

Od wersji Navigation 2.7.0-alpha01 ustawianie niestandardowych przejść, które wcześniej było możliwe w AnimatedNavHost, jest teraz obsługiwane bezpośrednio w NavHost. Więcej informacji znajdziesz w informacjach o wersji.

Więcej informacji

Więcej informacji o przechodzeniu na Navigation Compose znajdziesz w tych materiałach:

  • Ćwiczenie z zakresu Navigation Compose: poznaj podstawy Navigation Compose w ramach praktycznego ćwiczenia.
  • Now in Android repository: w pełni funkcjonalna aplikacja na Androida zbudowana w całości w Kotlinie i Jetpack Compose, która jest zgodna ze sprawdzonymi metodami projektowania i tworzenia aplikacji na Androida oraz zawiera Navigation Compose.
  • Migracja aplikacji Sunflower do Jetpack Compose: post na blogu, w którym opisujemy proces migracji aplikacji Sunflower z widoków do Compose, w tym do Navigation Compose.
  • Jetnews na każdym ekranie: post na blogu, w którym opisujemy refaktoryzację i migrację aplikacji Jetnews, aby obsługiwała wszystkie ekrany za pomocą Jetpack Compose i Navigation Compose.