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
확장 함수는 ConversationScreen
컴포저블을 표시하는 대상을 만듭니다. 또한 이제 화면 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( contactId ->
navController.navigateToContactDetails(id = contactId)
})
contactDetailsDestination()
}
}
요약
- 탐색 코드를 별도의 파일에 배치하여 관련 화면 세트의 탐색 코드를 캡슐화합니다.
NavGraphBuilder
에서 확장 함수를 만들어 대상 노출NavController
에서 확장 함수를 만들어 탐색 이벤트 노출internal
를 사용하여 화면 및 경로 유형을 비공개로 유지