탐색 코드 캡슐화

Kotlin DSL을 사용하여 그래프를 구성할 때 대상과 탐색 이벤트는 유지관리가 어려울 수 있습니다. 이것은 특히 여러 개의 독립적인 특성이 있는 경우에 더 그렇습니다.

대상 추출

대상을 NavGraphBuilder 확장 프로그램으로 이동해야 합니다. 함수와 비교합니다. 이들은 자신을 정의하는 경로 근처에 거주해야 하며 표시할 수 있습니다. 예를 들어 다음과 같은 앱 수준 코드를 연락처 목록을 표시하는 대상을 만듭니다.

// MyApp.kt

@Serializable
object Contacts

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

탐색 관련 코드를 별도의 파일로 이동해야 합니다.

// ContactsNavigation.kt

@Serializable
object Contacts

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

// MyApp.kt

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

이제 경로 및 대상 정의가 기본 앱과 별개이며 독립적으로 업데이트할 수 있습니다 기본 앱은 단일 앱에만 의존합니다. 확장 함수와 함께 사용합니다. 이 경우 NavGraphBuilder.contactsDestination()

NavGraphBuilder 확장 함수는 스테이트리스(Stateless) 및 화면 수준의 구성 가능한 함수와 탐색 관련 로직입니다. 이 레이어는 상태의 출처와 이벤트 처리 방법도 정의합니다.

다음 스니펫은 연락처의 기존 연락처 목록 대상을 업데이트하여 탐색 이벤트를 사용하여 연락처 세부정보를 표시합니다.

다음은 자체 모듈에 대해 internal할 수 있는 일반적인 화면 세트입니다. 다른 모듈에서 액세스할 수 없습니다.

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

대상 만들기

다음 NavGraphBuilder 확장 함수는 대상을 만듭니다. 이 코드는 ContactsScreen 컴포저블을 보여줍니다. 또한 이제 화면 UI 상태를 제공하고ViewModel 화면 관련 비즈니스 로직을 작성할 수 있습니다

연락처 세부정보 목적지로 이동과 같은 탐색 이벤트는 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
    )
  }
}

동일한 접근 방식을 사용하여 ContactDetailsScreen 이 경우 UI 상태를 가져오는 대신 NavBackStackEntry에서 직접 가져올 수 있습니다.

// ContactsNavigation.kt

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

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

탐색 이벤트 캡슐화

대상을 캡슐화하는 것과 같은 방식으로 탐색 이벤트를 사용하여 경로 유형을 불필요하게 노출하지 않도록 합니다. 수행 기준 NavController에서 확장 함수 만들기

// ContactsNavigation.kt

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

총정리

이제 연락처를 표시하기 위한 탐색 코드가 앱의 탐색 그래프에 포함되어 있습니다. 앱은 다음 요건을 충족해야 합니다.

  • NavGraphBuilder 확장 함수를 호출하여 대상을 만듭니다.
  • NavController 확장 함수를 호출하여 이러한 대상을 연결합니다. 탐색 이벤트
// MyApp.kt

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

요약

  • 관련 화면 세트의 탐색 코드를 배치하여 캡슐화합니다. 별도의 파일에 포함
  • NavGraphBuilder에서 확장 함수를 만들어 대상을 노출합니다.
  • NavController에서 확장 함수를 만들어 탐색 이벤트를 노출합니다.
  • internal를 사용하여 화면 및 경로 유형을 비공개로 유지하기