Otocz kod nawigacyjny

Przy tworzeniu grafu za pomocą lustrzanek DSL Kotlin przy zachowaniu miejsc docelowych zdarzenia nawigacji w jednym pliku mogą być trudne do zarządzania. To jest Zwłaszcza wtedy, gdy masz kilka niezależnych funkcji.

Wyodrębnij miejsca docelowe

Miejsca docelowe należy przenieść do rozszerzenia NavGraphBuilder funkcji. Powinny mieszkać blisko tras, które je definiują. wyświetlanych na nich ekranach. Przeanalizuj na przykład ten kod na poziomie aplikacji który tworzy miejsce docelowe z listą kontaktów:

// MyApp.kt

@Serializable
object Contacts

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

Kod związany z nawigacją należy przenieść do osobnego pliku:

// ContactsNavigation.kt

@Serializable
object Contacts

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

// MyApp.kt

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

Definicje tras i miejsc docelowych są teraz oddzielone od głównej aplikacji możesz je aktualizować. Główna aplikacja zależy tylko od . W tym przypadku jest to NavGraphBuilder.contactsDestination()

Funkcja rozszerzenia NavGraphBuilder tworzy pomost między bezstanowymi danymi funkcji kompozycyjnej na poziomie ekranu i logiki charakterystycznej dla Nawigacji. Ta warstwa może pozwalają też określić, skąd pochodzi dany stan i jak obsługujesz zdarzenia.

Przykład

Fragment tego fragmentu zawiera nowe miejsce docelowe, w którym będą wyświetlane i aktualizuje istniejący adres docelowy listy kontaktów, aby udostępnił zdarzenie nawigacji, by wyświetlić szczegóły kontaktu.

Oto typowy zestaw ekranów, który może internal wyświetlać się w osobnym module, więc których inne moduły nie mają dostępu:

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

Tworzenie miejsc docelowych

Ta funkcja rozszerzenia NavGraphBuilder tworzy miejsce docelowe który pokazuje funkcję ContactsScreen kompozycyjną. Oprócz tego łączy ekran z elementem ViewModel, który przedstawia stan UI i obsługuje logikę biznesową związaną z ekranami.

Zdarzenia związane z nawigacją, np. nawigacja do miejsca docelowego szczegółów kontaktu, są widoczne dla rozmówcy, a nie przez interfejs 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
    )
  }
}

W ten sam sposób możesz utworzyć miejsce docelowe z ContactDetailsScreen W tym przypadku zamiast pobierania stanu interfejsu użytkownika z model wyświetlania można pobrać bezpośrednio z NavBackStackEntry.

// ContactsNavigation.kt

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

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

Enkapuluj zdarzenia nawigacji

W ten sam sposób, w jaki zamykasz miejsca docelowe, możesz hermetyzować zdarzeń nawigacji, aby uniknąć niepotrzebnego ujawniania typów tras. Zrób to do tworzy funkcje rozszerzeń w klastrze NavController.

// ContactsNavigation.kt

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

Połącz możliwości

Kod nawigacyjny do wyświetlania kontaktów jest teraz wyraźnie oddzielony od na wykresie nawigacji. Aplikacja musi:

  • Wywołaj funkcje rozszerzenia NavGraphBuilder, aby utworzyć miejsca docelowe
  • Połącz te miejsca docelowe, wywołując funkcje rozszerzeń NavController dla zdarzeń nawigacji
// MyApp.kt

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

W skrócie

  • Umieść kod nawigacyjny dla powiązanego zestawu ekranów, umieszczając go w oddzielnym pliku
  • Aby udostępniać miejsca docelowe, utwórz w NavGraphBuilder funkcje rozszerzeń
  • Udostępnij zdarzenia nawigacji, tworząc funkcje rozszerzeń w: NavController
  • Aby zachować prywatność ekranów i rodzajów tras, użyj zasady internal