Encapsular seu código de navegação

Ao usar a DSL do Kotlin para construir seu gráfico, manter os destinos e eventos de navegação em um único arquivo pode ser difícil de manter. Isso é especialmente se você tiver vários atributos independentes.

Extrair destinos

Mova seus destinos para a extensão NavGraphBuilder . Eles devem residir perto das rotas que os definem, e os das telas que eles exibem. Por exemplo, considere o seguinte código no nível do app que cria um destino que mostra uma lista de contatos:

// MyApp.kt

@Serializable
object Contacts

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

Mova o código específico da navegação para um arquivo separado:

// ContactsNavigation.kt

@Serializable
object Contacts

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

// MyApp.kt

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

As definições de rotas e destinos agora estão separadas do app principal e você pode atualizá-los de modo independente. O app principal depende apenas de um função de extensão. Nesse caso, NavGraphBuilder.contactsDestination():

A função de extensão NavGraphBuilder forma a ponte entre um objeto sem estado função combinável da tela e lógica específica de navegação. Essa camada pode também definem de onde vem o estado e como você lida com eventos.

Exemplo

O snippet a seguir apresenta um novo destino para exibir a propriedade e atualiza o destino da lista de contatos existente para expor um evento de navegação para mostrar os detalhes do contato.

Este é um conjunto típico de telas que podem ser internal no próprio módulo. que outros módulos não possam acessá-los:

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

Criar destinos

A seguinte função de extensão NavGraphBuilder cria um destino que mostra o elemento combinável ContactsScreen. Além disso, ele agora conecta na tela com uma ViewModel que fornece o estado da interface da tela e processa as lógica de negócios relacionada à tela.

Eventos de navegação, como navegar até o destino dos detalhes do contato, expostos ao autor da chamada em vez de serem processados pelo 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
   
)
 
}
}

Você pode usar a mesma abordagem para criar um destino que exiba o ContactDetailsScreen: Nesse caso, em vez de obter o estado da interface de um de visualização, você pode obtê-lo diretamente do NavBackStackEntry.

// ContactsNavigation.kt

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

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

Encapsular eventos de navegação

Da mesma forma que você encapsula destinos, eventos de navegação para evitar a exposição desnecessária de tipos de trajeto. Faça isso até criando funções de extensão em NavController;

// ContactsNavigation.kt

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

Reúna tudo

O código de navegação para exibir contatos agora está claramente separado dos gráfico de navegação do app. O app precisa:

  • Chamar funções de extensão NavGraphBuilder para criar destinos
  • Conecte esses destinos chamando funções de extensão NavController. para eventos de navegação
// MyApp.kt

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

Resumo

  • Encapsule seu código de navegação para um conjunto de telas relacionadas colocando-o em um arquivo separado
  • Expor destinos criando funções de extensão em NavGraphBuilder
  • Expor eventos de navegação criando funções de extensão no NavController
  • Use internal para manter a privacidade de telas e tipos de trajeto