Modularizzare il codice di navigazione

Questa pagina è una guida per la modularizzazione del codice di navigazione. È inteso a integrare le indicazioni generali per la modularizzazione delle app.

Panoramica

La modularizzazione del codice di navigazione è il processo di separazione dei tasti di navigazione correlati e dei contenuti che rappresentano in moduli singoli. In questo modo, le responsabilità sono chiaramente separate e puoi navigare tra le diverse funzionalità dell'app.

Per modularizzare il codice di navigazione:

  • Crea due sottomoduli: api e impl per ogni funzionalità della tua app
  • Inserisci i tasti di navigazione per ogni funzionalità nel relativo modulo api
  • Inserisci entryProviders e contenuti navigabili per ogni funzionalità nel modulo impl associato.
  • Fornisci entryProviders ai moduli principali dell'app, direttamente o utilizzando l'inserimento delle dipendenze

Suddividere le funzionalità in moduli secondari di API e implementazione

Per ogni funzionalità della tua app, crea due sottomoduli denominati api e impl (abbreviazione di "implementazione"). Utilizza la tabella seguente per decidere dove inserire il codice di navigazione.

Nome modulo

Contiene

api

tasti di navigazione

impl

Contenuti per questa funzionalità, incluse le definizioni di NavEntry e entryProvider. Vedi anche Risolvere le chiavi per i contenuti.

Questo approccio consente a una funzionalità di passare a un'altra consentendo ai suoi contenuti, contenuti nel modulo impl, di dipendere dai tasti di navigazione di un altro modulo, contenuto nel modulo api.

Diagramma delle dipendenze dei moduli delle funzionalità che mostra come i moduli `impl` possono
dipendere dai moduli `api`.
Figura 1. Diagramma delle dipendenze dei moduli delle funzionalità che mostra come i moduli di implementazione possono dipendere dai moduli API.

Separare le voci di navigazione utilizzando le funzioni di estensione

In Navigazione 3, i contenuti navigabili vengono definiti utilizzando le voci di navigazione. Per separare queste voci in moduli distinti, crea funzioni di estensione su EntryProviderScope e spostale nel modulo impl per quella funzionalità. Questi sono noti come generatori di voci.

Il seguente esempio di codice mostra un generatore di voci che crea due voci di navigazione.

// import androidx.navigation3.runtime.EntryProviderScope
// import androidx.navigation3.runtime.NavKey

fun EntryProviderScope<NavKey>.featureAEntryBuilder() {
    entry<KeyA> {
        ContentRed("Screen A") {
            // Content for screen A
        }
    }
    entry<KeyA2> {
        ContentGreen("Screen A2") {
            // Content for screen A2
        }
    }
}

Chiama questa funzione utilizzando il DSL entryProvider quando definisci entryProvider nel modulo principale dell'app.

// import androidx.navigation3.runtime.entryProvider
// import androidx.navigation3.ui.NavDisplay
NavDisplay(
    entryProvider = entryProvider {
        featureAEntryBuilder()
    },
    // ...
)

Utilizzare l'inserimento delle dipendenze per aggiungere voci all'app principale

Nell'esempio di codice precedente, ogni generatore di voci viene chiamato direttamente dall'app principale utilizzando il linguaggio specifico del dominio entryProvider. Se la tua app ha molte schermate o moduli di funzionalità, questo potrebbe non essere scalabile.

Per risolvere il problema, fai in modo che ogni modulo delle funzionalità contribuisca con i propri builder di voci all'attività dell'app utilizzando l'inserimento delle dipendenze.

Ad esempio, il seguente codice utilizza Dagger multibindings, in particolare @IntoSet, per inserire i builder di voci in un Set di proprietà di MainActivity. Questi vengono poi chiamati in modo iterativo all'interno di entryProvider, eliminando la necessità di chiamare esplicitamente numerose funzioni di creazione delle voci.

Modulo delle funzionalità

// import dagger.Module
// import dagger.Provides
// import dagger.hilt.InstallIn
// import dagger.hilt.android.components.ActivityRetainedComponent
// import dagger.multibindings.IntoSet

@Module
@InstallIn(ActivityRetainedComponent::class)
object FeatureAModule {

    @IntoSet
    @Provides
    fun provideFeatureAEntryBuilder() : EntryProviderScope<NavKey>.() -> Unit = {
        featureAEntryBuilder()
    }
}

Modulo app

// import android.os.Bundle
// import androidx.activity.ComponentActivity
// import androidx.activity.compose.setContent
// import androidx.navigation3.runtime.EntryProviderScope
// import androidx.navigation3.runtime.NavKey
// import androidx.navigation3.runtime.entryProvider
// import androidx.navigation3.ui.NavDisplay
// import javax.inject.Inject

class MainActivity : ComponentActivity() {

    @Inject
    lateinit var entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<NavKey>.() -> Unit>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavDisplay(
                entryProvider = entryProvider {
                    entryBuilders.forEach { builder -> this.builder() }
                },
                // ...
            )
        }
    }
}

Se le voci di navigazione devono navigare, ad esempio contengono elementi dell'interfaccia utente che rimandano a nuove schermate, inserisci un oggetto in grado di modificare lo stato di navigazione dell'app in ogni funzione del builder.

Risorse

Per esempi di codice che mostrano come modularizzare il codice di Navigation 3, vedi: