[Android] Compose Navigation

sundays·2023년 4월 11일
0

jetpackcompose

목록 보기
6/7
post-custom-banner

이번에 앱을 전체적으로 개선하게 되면서 Compose Naviagtion을 도입하였습니다. 제가 이전에 개발하던 때만해도 너무 오래전이라 그땐 navigation의 개념자체도 없어서 일단 navigation이 무슨 일을 하는지에대해서도 알아보겠습니다.

Navigation

사용자가 앱 내의 여러 컨텐츠를 탐색하기 위해 상호작용 하는 것을 말합니다. 버튼 클릭 및 Navigation window 및 Appbar 와 같은 컴포넌트로 일관적인 사용자 환경을 제공하게 해줍니다

  • 네비게이션 패턴을 쉽게 설치 할 수 있습니다
  • backstack을 관리 할수 있습니다
  • fragment 트랜젝션을 자동화 합니다
  • typesafe-argument 를 사용하여 데이터 전달시 안정성 제공
  • 애니메이션 전환을 관리
  • 딥 링크를 간단히 구현 합니다

Gradle

dependencies {
    def nav_version = "2.5.3"

    implementation "androidx.navigation:navigation-compose:$nav_version"
}

NavController는 Navigation Component에서 가장 중요한 API입니다. composable의 앱내의 각각의 화면의 상태를 백스택에 유지하고 상태를 추적할 수 있습니다. 컴포저블의 rememberNavController() 메서드를 사용하여 생성할 수 있습니다.

val navController = rememberNavController()

각각의 NavController에는 한개의 NavHost Composable을 포함하고 있습니다. NavHost는 NavController와 연결해주는 Navigation 사이를 연결해주는 Composable destination의 그래프 를 명시하는 Navigation Graph가 있습니다. composable와 Navigation 사이에서 NavHost가 자동으로 recomposed되고 navigation graph 의 각 composable detination의 route가 연관이 되어 있습니다.

NavHost(navController = navController, startDestination = "profile") {
    composable("profile") { Profile(/*...*/) }
    composable("friendslist") { FriendsList(/*...*/) }
    /*...*/
}

Route는 Composable에서 String으로 정의되면서 특정한 destination에 implicit deep link를 가지는 각 detination는 항상 unique한 route를 가집니다.

navigation graph에 존재하는 composable destination을 탐색하기 위해서는 navigate method를 사용해야 하는데 하나의 단일 destination route를 포함합니다.

navController.navigate("friendslist")

기본적으로는 navigation은 back stack에 새로운 destination만 기록됩니다. navigation() 를 호출해서 navigation의 행동을 수정할 수도 있습니다.

// friendlist 로 이동하기 전에 backstack에서 home 까지 모든 항목을 꺼냅니다.
navController.navigation("friendslist") {
	popUpTo("home")
}

// friendlist 로 이동하기 전에 backstack에서 home 항목을 포함하여 모든 항목을 꺼냅니다.
navController.navigate("friendslist") {
    popUpTo("home") { inclusive = true }
}

// search 에 있는 경우 backstack상단에 같은 사본을 방지합니다.
navController.navigate("search") {
    launchSingleTop = true
}

기본적으로 모든 argments들은 String으로 파싱됩니다 composable() 내의 파라미터로 NamedNavArgument의 리스트들을 입력받습니다. navArgument 메서드에서 NameNavArgument의 타입과 메서드를 지정할 수 있습니다.

NavHost(startDestination = "profile/{userId}") {
    ...
    composable(
        "profile/{userId}",
        arguments = listOf(navArgument("userId") { type = NavType.StringType })
    ) { backStackEntry ->
    	Profile(navController, backStackEntry.arguments?.getString("userId"))
    }
}

네비게이션으로 파라미터를 넘길때는 다음과 같은 예시로 보낼 수 있습니다.

navController.navigate("profile/user1234")

viewModel

만약 userId값의 destination 의 argument에서 중요한 정보일 경우에는 viewmodel에서 다음과 같이 viewmodel의 savedstatehandle을 사용하여 데이터의 레이어의 접근의 책임을 방지할 수도 있습니다.

class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {

    private val userId: String = checkNotNull(savedStateHandle["userId"])

    // 데이터 레이어에서 userId가 기본적인 사용자 정보를 가지고 있을때 
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)
	...
}

결론

간단하게 Navigation을 사용하는 법을 알게되었습니다. 프로젝트에 도입하게 되면서 느끼는건데.. 지금은 화면이 많지 않지만 결국에는 Navigation을 관리하는 것도 상당한 일이 될 듯 싶습니다. 간단하게 Navigation 을 사용한 예시를 확인하시고 싶으시면 저의 Github를 참고 하세요

Reference

profile
develop life
post-custom-banner

0개의 댓글