TIL240510 Navigation

jericho·2024년 5월 11일

TIL

목록 보기
59/62

탐색 (Navigation)

https://developer.android.com/guide/navigation?hl=ko

탐색에 필요한 종속성

dependencies {
  val nav_version = "2.7.7"

  // Java language implementation
  implementation("androidx.navigation:navigation-fragment:$nav_version")
  implementation("androidx.navigation:navigation-ui:$nav_version")

  // Kotlin
  implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
  implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

  // Feature module Support
  implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")

  // Testing Navigation
  androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")

  // Jetpack Compose Integration
  implementation("androidx.navigation:navigation-compose:$nav_version")
}

탐색 컨트롤러 만들기 (NavController)

https://developer.android.com/guide/navigation/navcontroller?hl=ko

val navController = rememberNavController()

컴포저블 계층 구조에서 높은 수준의 NavController를 만들어야 한다. 이 값은 이를 참조해야 하는 모든 컴포저블이 할 수 있을 만큼 충분히 높아야 한다.

이렇게 하면 NavController를 화면 외부에서 컴포저블을 업데이트하기 위한 단일 정보 소스로 사용할 수 있다. 이는 상태 호이스팅의 원칙을 따른다.

참고로 뷰 UI 프레임워크를 사용하는 경우, 컨텍스트에 따라 다음과 같이 NavController를 검색할 수 있다.
(일반적으로 먼저 NavHostFragment를 가져온 다음 프래그먼트에서 NavController를 검색함)

Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)

// 예시코드
val navHostFragment =
    supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

탐색 그래프 설계 (NavGraph)

https://developer.android.com/guide/navigation/design?hl=ko

Destination 에는 세 가지 일반적인 타입이 있다 - Hosted, Dialog, Activity
호스팅이 가장 일반적이고 기본적.

사용하는 UI 프레임워크에 따라 (컴포즈 or 프래그먼트) 탐색 호스트와 그래프를 만드는 방법이 다름.
여기에서는 컴포즈만 다루겠다.
(필요하다면 https://developer.android.com/guide/navigation/design?hl=ko#frameworkshttps://developer.android.com/guide/navigation/use-graph/navigate?hl=ko#id 참고)

NavHost 컴포저블을 사용하고, Kotlin DSL을 사용하여 NavGraph를 만든다.
NavHost를 추가하는 과정에서 탐색 그래프를 직접 구성할 수 있고, NavController.createGraph() 메서드를 사용하여 NavGraph를 만들어 NavHost에 직접 전달하는 프로그래매틱 방식도 가능하다.

val navController = rememberNavController()

NavHost(navController = navController, startDestination = "profile") {
    composable("profile") { Profile( /* ... */ ) }
    composable("friendslist") { FriendsList( /* ... */ ) }
    // Add more destinations similarly.
}
  • NavHost에 전달된 람다는 최종적으로 NavController.creatGraph()를 호출하고 NavGraph를 반환한다.
  • NavGraphBuilder.composable()를 호출하면 결과 NavGraph에 대상이 추가됨.
  • 위 예시에서 대상(destination)은 Profile 및 FriendsList 컴포저블이다. 경로 문자열 "profile"와 "friendslist"가 두 대상을 식별하는 키가 된다.

위 예시를 NavGraph를 별도로 만들어 NavHost에 직접 전달하는 프로그래매틱 방식으로 하면 다음과 같이 된다.

val navGraph by remember(navController) {
  navController.createGraph(startDestination = "profile") {
    composable("profile") { Profile() }
    composable("friendslist") { FriendsList() }
  }
}
NavHost(navController, navGraph)
  • 중요: NavController는 단일 NavHost 컴포저블과 연결되고, NavHost는 탐색 그래프에 대한 NavController 액세스를 제공한다. NavController를 사용하여 대상으로 이동하면 NavController가 연결된 NavHost와 상호작용하게 된다.
// 예시 코드

// Define the Profile composable.
@Composable
fun Profile(onNavigateToFriendsList: () -> Unit) {
  Text("Profile")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// Define the FriendsList composable.
@Composable
fun FriendsList(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = "profile") {
    composable("profile") { Profile(onNavigateToFriendsList = { navController.navigate("friendslist") }) }
    composable("friendslist") { FriendsList(onNavigateToProfile = { navController.navigate("profile") }) }
  }
}

예시에서 볼 수 있듯이 NavController를 컴포저블에 전달하는 대신 NavHost에 이벤트를 노출한다. 즉, 컴포저블에는 NavHost가 NavController.navigate()를 호출하는 람다를 전달하는 () -> Unit 유형의 매개변수가 있어야 한다.
(참고: 람다에서 추가 데이터를 가져오도록 하려면 (String) -> Unit와 같은 매개변수 유형을 사용하면 된다. 컴포저블에서 여러 매개변수를 사용할 수도 있다.)

(중첩 그래프를 사용할 수도 있다. 여기에는 그래프를 탐색 대상으로 사용하는 작업이 포함된다. 자세한 내용은 https://developer.android.com/guide/navigation/design/nested-graphs?hl=ko 참고)

대상으로 이동 (NavController.navigate())

https://developer.android.com/guide/navigation/use-graph/navigate?hl=ko

NavController.navigate() 를 사용하여 탐색할 수 있다.
오버로드가 많은데, 선택해야 하는 오버로드는 정확한 컨텍스트에 해당한다.

컴포저블로 이동하려면 NavController.navigate(route) 를 사용한다. route는 String이며 대상의 키 역할을 함.
route 문자열을 사용하여 이동하려면 먼저 각 대상이 route와 연결되도록 NavGraph를 만들어야 한다. (composable() 해서 만든거)

navigate()를 직접 호출하도록 NavController 참조를 전달하면 안 된다.
단방향 데이터 흐름(UDF) 원칙에 따라 컴포저블은 대신 NavController가 처리하는 이벤트를 노출해야 한다. (navigate 호출을 람다식으로 전달)

// 예시 코드

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "profile"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("profile") {
            ProfileScreen(
                onNavigateToFriends = { navController.navigate("friendsList") },
                /*...*/
            )
        }
        composable("friendslist") { FriendsListScreen(/*...*/) }
    }
}

@Composable
fun ProfileScreen(
    onNavigateToFriends: () -> Unit,
    /*...*/
) {
    /*...*/
    Button(onClick = onNavigateToFriends) {
        Text(text = "See friends list")
    }
}
  1. MyAppNavHost 컴포저블에는 NavController 인스턴스가 있다.
  2. 따라서 navigate() 호출은 ProfileScreen과 같은 하위 컴포저블이 아니라 여기서 발생해야 한다.
  3. ProfileScreen에는 FriendsList로 이동하는 버튼이 있지만 navigate()를 직접 호출하지는 않는다.
  4. 대신 버튼은 onNavigateToFriends 매개변수로 노출되는 함수를 호출한다.
  5. MyAppNavHost가 탐색 그래프에 ProfileScreen을 추가하면서 onNavigateToFriends 매개변수에 navigate()를 호출하는 람다를 전달한다.

(경고: navigate()는 컴포저블 자체의 일부가 아니라 콜백의 일부로만 호출해야 한다. 이렇게 하면 매 리컴포지션마다 navigate()가 호출되지 않는다.)


참고
https://developer.android.com/develop/ui/compose/navigation?hl=ko
https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation?hl=ko#0

0개의 댓글