Incapsula il tuo codice di navigazione

Quando utilizzi la Kotlin DSL per costruire il tuo grafico, mantenendo le destinazioni e gli eventi di navigazione in un unico file possono essere difficili da gestire. Questo è soprattutto se hai più caratteristiche indipendenti.

Estrarre le destinazioni

Devi spostare le destinazioni nell'estensione NavGraphBuilder funzioni. Devono trovarsi vicino alle route che li definiscono e schermate visualizzate. Ad esempio, considera il seguente codice a livello di app che crea una destinazione che mostra un elenco di contatti:

// MyApp.kt

@Serializable
object Contacts

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
  }
}

Dovresti spostare il codice specifico per la navigazione in un file separato:

// ContactsNavigation.kt

@Serializable
object Contacts

fun NavGraphBuilder.contactsDestination() {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
}

// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination()
  }
}

I percorsi e le definizioni delle destinazioni sono ora separati dall'app principale e puoi aggiornarli singolarmente. L'app principale dipende soltanto da una singola di estensione. In questo caso, NavGraphBuilder.contactsDestination().

La funzione di estensione NavGraphBuilder costituisce il ponte tra una rete stateless funzione componibile a livello di schermo e logica specifica per la navigazione. Questo livello può definiscono anche la provenienza dello stato e il modo in cui vengono gestiti gli eventi.

Esempio

Il seguente snippet introduce una nuova destinazione per visualizzare e aggiorna la destinazione esistente dell'elenco contatti per esporre un evento di navigazione per visualizzare i dettagli del contatto.

Di seguito è riportato un tipico set di schermate che possono essere internal per il proprio modulo, quindi che gli altri moduli non possono accedervi:

// ContactScreens.kt

// Displays a list of contacts
@Composable
internal fun ContactsScreen(
  uiState: ContactsUiState,
  onNavigateToContactDetails: (contactId: String) -> Unit
) { ... }

// Displays the details for an individual contact
@Composable
internal fun ContactDetailsScreen(contact: ContactDetails) { ... }

Crea destinazioni

La seguente funzione di estensione NavGraphBuilder crea una destinazione che mostra il componibile ContactsScreen. Inoltre, ora collega schermata con un ViewModel che fornisce lo stato UI della schermata e gestisce della logica di business relativa allo schermo.

Gli eventi di navigazione, come la navigazione verso la destinazione dei dati di contatto, vengono esposto al chiamante anziché essere gestito da ViewModel.

// ContactsNavigation.kt

@Serializable
object Contacts

// Adds contacts destination to `this` NavGraphBuilder
fun NavGraphBuilder.contactsDestination(
  // Navigation events are exposed to the caller to be handled at a higher level
  onNavigateToContactDetails: (contactId: String) -> Unit
) {
  composable<Contacts> {
    // The ViewModel as a screen level state holder produces the screen
    // UI state and handles business logic for the ConversationScreen
    val viewModel: ContactsViewModel = hiltViewModel()
    val uiState = viewModel.uiState.collectAsStateWithLifecycle()
    ContactsScreen(
      uiState,
      onNavigateToContactDetails
    )
  }
}

Puoi usare lo stesso approccio per creare una destinazione che mostri i ContactDetailsScreen. In questo caso, invece di ottenere lo stato dell'interfaccia utente da un di grandi dimensioni, puoi ottenerlo direttamente dal NavBackStackEntry.

// ContactsNavigation.kt

@Serializable
internal data class ContactDetails(val id: String)

fun NavGraphBuilder.contactDetailsScreen() {
  composable<ContactDetails> { navBackStackEntry ->
    ContactDetailsScreen(contact = navBackStackEntry.toRoute())
  }
}

Eventi di navigazione incapsulati

Analogamente a quanto accade per l'incapsulamento delle destinazioni, eventi di navigazione per evitare di esporre inutilmente i tipi di percorso. Per farlo creazione di funzioni di estensione su NavController.

// ContactsNavigation.kt

fun NavController.navigateToContactDetails(id: String) {
  navigate(route = ContactDetails(id = id))
}

Riunisci

Il codice di navigazione per visualizzare i contatti è ora ben separato dal grafico di navigazione dell'app. L'app deve:

  • Chiama le funzioni dell'estensione NavGraphBuilder per creare destinazioni
  • Collega queste destinazioni chiamando le funzioni dell'estensione di NavController per eventi di navigazione
// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination(onNavigateToContactDetails = { contactId ->
        navController.navigateToContactDetails(id = contactId)
     })
     contactDetailsDestination()
  }
}

In sintesi

  • Incapsula il codice di navigazione per un insieme di schermate correlato inserendolo in un file separato
  • Esponi le destinazioni creando funzioni di estensione su NavGraphBuilder
  • Esponi gli eventi di navigazione creando funzioni di estensione su NavController
  • Usa internal per mantenere privati i tipi di schermate e route