Esegui la migrazione di Jetpack Navigation a Navigation Compose

L'API Navigation Compose ti consente di spostarti tra i composabili in un'app Compose, sfruttando al contempo il componente, l'infrastruttura e le funzionalità di Jetpack Navigation.

Questa pagina descrive come eseguire la migrazione da Jetpack Navigation basato su frammenti a Navigation Compose, nell'ambito della migrazione più ampia dell'interfaccia utente basata su visualizzazioni a Jetpack Compose.

Prerequisiti per la migrazione

Puoi eseguire la migrazione a Compose di navigazione dopo essere in grado di sostituire tutti i tuoi Frammenti con gli elementi componibili corrispondenti dello schermo. I composabili della schermata possono contenere un insieme di contenuti di Compose e View, ma tutte le destinazioni di navigazione devono essere composabili per abilitare la migrazione di Compose di navigazione. Fino ad allora, devi continuare a utilizzare il componente di navigazione basato su frammenti nel codice base di Compose e della visualizzazione di interoperabilità. Per ulteriori informazioni, consulta la documentazione sull'interoperabilità della navigazione.

L'utilizzo di Componi navigazione in un'app solo di Componi non è un prerequisito. Puoi continuare a utilizzare il componente di navigazione basato su frammenti, a condizione di mantenere i frammenti per ospitare i tuoi contenuti componibili.

Passaggi per la migrazione

Sia che tu stia seguendo la nostra strategia di migrazione consigliata o stia adottando un altro approccio, raggiungerai un punto in cui tutte le destinazioni di navigazione sono componibili sullo schermo, in cui i frammenti agiscono solo come container componibili. A questo livello, puoi eseguire la migrazione a Scrittura con navigazione.

Se la tua app segue già un pattern di progettazione UDF e la nostra guida all'architettura, la migrazione a Jetpack Compose e Navigation Compose non dovrebbe richiedere ristrutturazioni importanti di altri livelli dell'app, a parte il livello dell'interfaccia utente.

Per eseguire la migrazione a Navigatore Compose, segui questi passaggi:

  1. Aggiungi la dipendenza Navigation Compose alla tua app.
  2. Crea un composable App-level e aggiungilo a Activity come punto di accesso di Compose, sostituendo la configurazione del layout della vista:

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

  3. Crea tipi per ogni destinazione di navigazione. Utilizza un data object per le destinazioni che non richiedono dati e data class o class per le destinazioni che richiedono dati.

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

  4. Configura NavController in un punto a cui hanno accesso tutti i composabili che devono farvi riferimento (di solito all'interno del composable App). Questo approccio segue i principi del sollevamento dello stato e consente di utilizzare NavController come fonte attendibile per navigare tra le schermate componibili e mantenere lo stack posteriore:

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

  5. Crea l'elemento NavHost della tua app all'interno del composable App e passa il navController:

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

  6. Aggiungi le destinazioni composable per creare il grafo di navigazione. Se per ogni schermata è stata eseguita in precedenza la migrazione a Compose, questo passaggio consiste solo nell'estrarre questi composabili della schermata dai frammenti nelle composable destinazioni:

    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. Se hai seguito le indicazioni sulla progettazione dell'interfaccia utente di Compose, nello specifico su come trasmettere ViewModel e eventi di navigazione ai componenti componibili, il passaggio successivo è modificare il modo in cui fornisci ViewModel a ogni schermata componibile. Spesso puoi usare l'inserimento di Hilt e il suo punto di integrazione con Compose e Navigazione tramite hiltViewModel:

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

  8. Sostituisci tutte le chiamate di navigazione findNavController() con quelle navController e passale come eventi di navigazione a ogni schermata componibile, anziché trasmettere l'intera chiamata navController. Questo approccio segue le best practice di esposizione degli eventi delle funzioni componibili agli utenti chiamanti e mantiene navController come unica fonte attendibile.

    I dati possono essere passati a una destinazione creando un'istanza della classe route definita per quella destinazione. Può essere ottenuto direttamente dalla voce dello stack precedente nella destinazione o da un ViewModel utilizzando 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. Rimuovi tutti i frammenti, i layout XML pertinenti, la navigazione e altre risorse non necessarie, nonché le dipendenze obsolete di Fragment e Jetpack Navigation.

Puoi trovare gli stessi passaggi con ulteriori dettagli relativi a Componi navigazione nella documentazione di configurazione.

Casi d'uso comuni

Indipendentemente dal componente di navigazione utilizzato, si applicano gli stesse linee guida per la navigazione.

Ecco alcuni casi d'uso comuni durante la migrazione:

Per informazioni più dettagliate su questi casi d'uso, consulta Navigare con Compose.

Recuperare dati complessi durante la navigazione

Ti consigliamo vivamente di non trasmettere oggetti di dati complessi durante la navigazione. Passa invece le informazioni minime necessarie, ad esempio un identificatore univoco o un'altra forma di ID, come argomenti quando esegui azioni di navigazione. Devi memorizzare gli oggetti complessi come dati in un'unica fonte attendibile, ad esempio il livello di dati. Per ulteriori informazioni, vedi Recupero di dati complessi durante la navigazione.

Se i tuoi frammenti passano oggetti complessi come argomenti, ti consigliamo di eseguire prima il refactoring del codice in modo da poter memorizzare e recuperare questi oggetti dal livello dati. Consulta il repository Ora su Android per alcuni esempi.

Limitazioni

Questa sezione descrive le limitazioni attuali di Compose Navigazione.

Migrazione incrementale a Navigation Compose

Al momento, non puoi utilizzare Navigation Compose continuando a utilizzare i frammenti come destinazioni nel codice. Per iniziare a utilizzare Navigation Compose, tutte le tue destinazioni devono essere componibili. Puoi monitorare questa richiesta di funzionalità nell'Issue Tracker.

Animazioni di transizione

A partire da Navigation 2.7.0-alpha01, il supporto per l'impostazione di transizioni personalizzate, precedentemente disponibile in AnimatedNavHost, è ora supportato direttamente in NavHost. Per ulteriori informazioni, leggi le note di rilascio.

Scopri di più

Per ulteriori informazioni sulla migrazione a Composizione di navigazione, consulta le seguenti risorse:

  • Codelab Navigation Compose: apprendi le nozioni di base di Navigation Compose con un codelab pratico.
  • Ora nel repository Android: un'app Android completamente funzionale realizzata interamente con Kotlin e Jetpack Compose, che segue le best practice di progettazione e sviluppo di Android e include Navigation Compose.
  • Migrazione di Sunflower a Jetpack Compose: un post del blog che documenta il percorso di migrazione dell'app di esempio Sunflower da Views a Compose, che include anche la migrazione a Navigation Compose.
  • Jetnews per ogni schermo: un post del blog che descrive il refactoring e la migrazione del Sample Jetnews per supportare tutte le schermate con Jetpack Compose e Navigation Compose.