Navigationscode einbauen

Wenn Sie Ihre Grafik mit Kotlin DSL erstellen, Es kann schwierig sein, Navigationsereignisse in einer einzigen Datei zu verwalten. Dies ist Dies gilt insbesondere, wenn Sie mehrere unabhängige Funktionen haben.

Ziele extrahieren

Sie sollten Ihre Ziele in die Erweiterung NavGraphBuilder verschieben Funktionen. Sie sollten in der Nähe der Routen wohnen, auf denen sie definiert sind, und Bildschirmen verwendet werden. Betrachten Sie zum Beispiel den folgenden Code auf App-Ebene erstellt. Ein Ziel mit einer Liste von Kontakten wird erstellt:

// MyApp.kt

@Serializable
object Contacts

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

Sie sollten den Navigations-spezifischen Code in eine separate Datei verschieben:

// ContactsNavigation.kt

@Serializable
object Contacts

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

// MyApp.kt

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

Die Routen- und Zieldefinitionen sind nun getrennt von der Haupt-App und unabhängig voneinander aktualisieren. Die Haupt-App ist nur von einem Erweiterungsfunktion. In diesem Fall ist das NavGraphBuilder.contactsDestination()

Die Erweiterungsfunktion NavGraphBuilder bildet die Brücke zwischen einem zustandslosen zusammensetzbare Funktion auf Bildschirmebene und navigationsspezifische Logik. Diese Ebene kann und definieren, woher der Status stammt und wie Sie Ereignisse verarbeiten.

Beispiel

Mit dem folgenden Snippet wird ein neues Ziel zum Anzeigen der und aktualisiert das Ziel der bestehenden Kontaktliste, um Navigationsereignis, um die Details des Kontakts anzuzeigen.

Hier ist ein typischer Satz von Bildschirmen, die internal in ein eigenes Modul gebracht werden können. dass andere Module nicht darauf zugreifen können:

// 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) { ... }

Ziele erstellen

Mit der folgenden NavGraphBuilder-Erweiterungsfunktion wird ein Ziel erstellt. wo die zusammensetzbare Funktion ContactsScreen angezeigt wird. Außerdem verbindet es jetzt dem Bildschirm mit einem ViewModel, der den Status der Bildschirm-UI angibt und bildschirmbezogene Geschäftslogik.

Navigationsereignisse, z. B. das Navigieren zum Ziel der Kontaktdetails, sind dem Aufrufer angezeigt und nicht vom ViewModel verarbeitet.

// 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
    )
  }
}

Auf dieselbe Weise können Sie auch ein Ziel erstellen, das die ContactDetailsScreen In diesem Fall wird der UI-Status nicht von einem erhalten Sie direkt im NavBackStackEntry.

// ContactsNavigation.kt

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

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

Navigationsereignisse zusammenfassen

So wie Sie Ziele verkapseln, können Sie auch Navigationsereignisse ein, um zu vermeiden, dass Routentypen unnötigerweise offengelegt werden. Vorgehensweise Erweiterungsfunktionen werden für NavController erstellt.

// ContactsNavigation.kt

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

Zusammenführung

Der Navigationscode für die Anzeige von Kontakten ist jetzt sauber vom im Navigationsdiagramm der App. Die App muss folgende Voraussetzungen erfüllen:

  • NavGraphBuilder-Erweiterungsfunktionen aufrufen, um Ziele zu erstellen
  • Verbinden Sie diese Ziele, indem Sie NavController-Erweiterungsfunktionen aufrufen für Navigationsereignisse
// MyApp.kt

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

Zusammenfassung

  • Kapseln Sie Ihren Navigationscode für einen zusammengehörigen Satz von Bildschirmen, indem Sie ihn in einer separaten Datei
  • Ziele durch Erstellen von Erweiterungsfunktionen in NavGraphBuilder freigeben
  • Navigationsereignisse durch Erstellen von Erweiterungsfunktionen für NavController freigeben
  • Mit internal können Sie Bildschirme und Routentypen privat halten