Android Dev/Compose / / 2021. 8. 11. 21:09

JetPack Compose(2) - Navigation

 

 

나.이.만.나도 이제 만날래!

셀소 방식의 신개념 파티앱이에요 당신의 인연. 더 이상 미룰 수 없어요!!!! 셀프 소개팅, 미팅룸을 열고 참여하여 내 인연을 스스로 만들어가요. 내가 선택한 장소에서 직접 파티 룸을 열어 셀

vlm-naiman.tistory.com

 

 

 

 

Jetpack Compose Navigation  |  Android 개발자  |  Android Developers

In this codelab, you’ll learn about the features of using Navigation in Compose.

developer.android.com

 

3. Migrate to Navigation

 

Rally is an existing app which initially doesn't use Navigation. The migration follows several steps:

  1. Add the Navigation dependency
  2. Set up the NavController and NavHost
  3. Prepare routes for destinations
  4. Replace the original destination mechanism with navigation routes

 

Add the Navigation dependency

Open the app's build file, found at app/build.gradle. In the dependencies section, add the navigation-compose dependency.

 

dependencies { implementation "androidx.navigation:navigation-compose:2.4.0-alpha04" // other dependencies }

 

위 디펜던시를 추가한다.

 

The NavController is the central component when using Navigation in Compose; it keeps track of back stack entries, moves the stack forward, enables back stack manipulation, and navigating between screen states. Because NavController is central to navigation it has to be created first in order to navigate to destinations.

 

NavController 는 컴포즈에서 네비게이션을 쓸때 사용되는 메인 컴포넌트이다. 이는 스택을 관리하는 역할을 하며, screen state 사이를 네비게이팅할때 각 스텍을 tracking한다.

NavController 는 탐색하는데 있어서 center이며, destinations에 도달하기 위해 가장 먼저 만들어져야한다.

 

 

Within Compose you're working with a NavHostController, which is a subclass of NavController. Obtain a NavController by using the rememberNavController() function; this creates and remembers a NavController which survives configuration changes (using rememberSavable). The NavController is associated with a single NavHost composable. The NavHost links the NavController with a navigation graph where composable destinations are specified.

 

NavController의 서브클래스인 NavHostController로 작업한다.

rememberNavController()를 사용하여 NavController를 얻는다. 이것이 NavController 를 만들고 remember하며, configuration이 바뀔 지라도 살아남게 된다. NavController는 single NavHost composable와 연관되어있다.

NavHost 는 NavController와 Navigation Graph를 연결시켜서 composable의 destination을 특정시킨다.

 

RallyApp안에 NavController 를 설정해준다. 이것이 composable의 root가 된다.

 

 

 

위 3 화면중 Overview를 start Destination으로 정할 것이다.

Compose로 Navigation을 사용할때, ruotes를 String으로 표현할 수 있다.

이러한 strings를 URL이나 Deeplinks 와 같은 것으로 생각할 수 있다.

In this codelab we'll use the name property of each RallyScreen item as the route, for example, RallyScreen.Overview.name.

 

 

Preparation

Go back to the RallyApp composable in RallyActivity.kt and replace Box containing the screen's contents with a newly created NavHost. Pass in the navController we created in the previous step. The NavHost also needs a startDestination. Set it to RallyScreen.Overview.name. Also, create a Modifier to pass the padding into the NavHost.

 

기존코드의 Box부분을 NavHost로 교체한다.

위를 아래로 교체

 

Now we can define our nav graph. The destinations that the NavHost can navigate to are ready to accept destinations. We do this using a NavGraphBuilder, which is provided to the last parameter of NavHost; a lambda for defining your graph. As this parameter expects a function you can declare destinations in a trailing lambda. The Navigation Compose artifact provides the NavGraphBuilder.composable extension function. Use it to define navigation destinations in your graph.

 

위 코드에서 Navhost() { }  의 람다식은 NavGraphBuilder를 제공한다. 여기에 destination을 정의하면되는데, NavGraphbuilder.compsable 확장함수가 이를 제공하며 아래와 같이 구성하면 된다.

 

먼저 currentScrrent.content를 제거하고 Text를 간단히 띄워보면 아래와 같이 표시가 되는것을 확인할 수 있다.

 

위와 같이 코드 수정후 실행시 결과는 아래와 같다

 

 

RallyTabRow composable은 tab클릭의 콜벡이며, onTabSelected를 호출한다. Update the selection code to use navController for navigating to the selected screen.

 

This is all that's necessary to navigate to a screen via the TabRow using navigation:

아래와 같이 코드를 변경한다

 

With this change, currentScreen will no longer be updated. This means that expanding and collapsing of selected items won't work. To re-enable this behavior, the currentScreen property needs to be updated as well. Luckily Navigation holds on to the back stack for you and can provide you with the current back stack entry as a State. With this State you can react to changes to the back stack. You can even query the current back stack entry for its route.

 

위와 같이 변경하면 currentScreen은 더이상 업데이트 되지 않으며, expanding, collapsing에 더이상 working하지 않는다는 의미가 된다.

운이 좋게도, navigation은 back stack을 가지고 있으며, state로서 현재의 back stack을 제공받을 수 있다. 이를 활용할 것이다.

 

아래와 같이 코드 수정을 한다.

 

현재까지의 full code는 아래와 같다

 


@Composable
fun RallyApp() {
RallyTheme {
val allScreens = RallyScreen.values().toList()
val navController = rememberNavController()
val backstackEntry = navController.currentBackStackEntryAsState()
// var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) }
val currentScreen = RallyScreen.fromRoute(
backstackEntry.value?.destination?.route
)
Scaffold(
topBar = {
RallyTabRow(
allScreens = allScreens,
//onTabSelected = { screen -> currentScreen = screen },
onTabSelected = { screen -> navController.navigate(screen.name)},
currentScreen = currentScreen
)
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = RallyScreen.Overview.name,
modifier = Modifier.padding(innerPadding)) {

composable(RallyScreen.Overview.name) {
Text(RallyScreen.Overview.name)
}
composable(RallyScreen.Accounts.name) {
Text(RallyScreen.Accounts.name)
}
composable(RallyScreen.Bills.name) {
Text(RallyScreen.Bills.name)
}

}
}
}
}

 

 

 

Migrate RallyScreen to Navigation

After you have completed this step, the composable will be completely decoupled from RallyScreen enum and moved into the NavHost. RallyScreen will only exist to provide an icon and title for the screen.

 

Open RallyScreen.kt. Move each screen's implementation of body into the corresponding composables within your NavHost in RallyApp.

 

아래와 같이 코드 수정을 each scrren의 임플리멘테이션 바디를 NavHost에 전달한다.

 

At this point you can safely remove the content function and body parameter and its usages from RallyScreen which will leave you with this code:

기존에 enum에서 정의되어있던 body를 받는 content 메소드를 제거한다.

위와 같이 수정하면 backstack이 지원되므로 back button은 사용자가 원하는 결과를 보여줄 수 있다.

 

 

Enable clicks on OverviewScreen

 

 

위 페이지에서 SEE ALL 버튼을 구현해본다.

OverviewBody can accept several functions as callbacks to click events. Let's implement onClickSeeAllAccounts and onClickSeeAllBills to navigate to relevant destinations.

 

OverviewBody can accept several functions as callbacks to click events. Let's implement onClickSeeAllAccounts and onClickSeeAllBills to navigate to relevant destinations.

To enable navigation when the "see all" button is clicked, use the navController and navigate to either the Accounts or Bills screen. Open RallyActivity.kt, find OverviewBody within NavHost and add the navigation calls.

 

 

위와 같이 navController를 사용하여 navigating 할 수 있다

 

Now it has become possible to easily change the behavior of click events for OverviewBody. Keeping the navController at the top level of your navigation hierarchy and not passing it directly into OverviewBody makes it easy to preview or test OverviewBody in isolation, without having to rely on an actual navController being present when doing so.

 

4. Navigating with arguments

 

Jetpack Compose Navigation  |  Android 개발자  |  Android Developers

In this codelab, you’ll learn about the features of using Navigation in Compose.

developer.android.com

Let's add some new functionality to Rally! We'll add an Accounts screen which shows details of an individual account when a row is clicked.

A navigation argument makes the route dynamic. Navigation arguments are a very powerful tool to make routing behavior dynamic by passing one or more arguments into a route and adjusting argument types or default values.

 

named argument는 kotlin syntax 문법 ${ }과 비슷한 꼴이다.

 

composable의 body는 파라미터를 받는데.. 이는 이제까지의 설명에 있어서는 잘 사용하지 않았던 부분이다.

그 중 파라미터 NavBackStackEntry는 현재 destination의 인자들과 route를 모델링한다.

예를들어, 위 코드에서 arguments는 selected account의 이름과 같은 것을 retrieve하는데 사용할 수 있으며, UserData를 보고 SingleAccountBody composable에 전달한다.

 

 

You also could provide a default value to use if the argument has not been provided. We'll skip that because it's not necessary here.

 

Now that the composable is set up with the argument, you can navigate to it, using the navController like this: navController.navigate("${RallyScreen.Accounts.name}/$accountName").

Add this function to the onAccountClick parameter of OverviewBody's declaration in NavHost and to onAccountClick of AccountsBody.

To keep things reusable you could create a private helper function like below.

 

이제 위에서 정의해둔 SingleAccountBody로 네비게이트 할 수 있다.

아래의 코드를 확인하라.

 

 

위와 같이 구성하면 아래와 같이 실행할 수 있다

 

 

5. Enable deep link support

In addition to arguments you can also use deep links to expose destinations in your app to third party apps. In this section you'll add a new deep link to the route created in the previous section, enabling deep links from outside your app to individual accounts directly by name.

 

6. Extract finished NavHost

Now your NavHost is complete. You can extract it from the RallyApp composable to its own function and call it RallyNavHost. This is the one and only composable you should work directly with the navController. By not creating the navController within RallyNavHost, you can still use it to make tab selection, which is part of the higher structure, within RallyApp.

RallyNavHost를 만들어서 navController를 직접 만들지 않으면서도 higher 구조에서 RallyApp을 포함시킬수 있도록 만들 수 있다.

 

@Composable
fun RallyNavHost(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = RallyScreen.Overview.name,
modifier = modifier
) {
composable(RallyScreen.Overview.name) {
OverviewBody(
onClickSeeAllAccounts = { navController.navigate(RallyScreen.Accounts.name) },
onClickSeeAllBills = { navController.navigate(RallyScreen.Bills.name) },
onAccountClick = { name ->
navController.navigate("${RallyScreen.Accounts.name}/$name")
},
)
}
composable(RallyScreen.Accounts.name) {
AccountsBody(accounts = UserData.accounts) { name ->
navController.navigate("Accounts/${name}")
}
}
composable(RallyScreen.Bills.name) {
BillsBody(bills = UserData.bills)
}
val accountsName = RallyScreen.Accounts.name
composable(
"$accountsName/{name}",
arguments = listOf(
navArgument("name") {
type = NavType.StringType
},
),
deepLinks = listOf(navDeepLink {
uriPattern = "example://rally/$accountsName/{name}"
}),
) { entry ->
val accountName = entry.arguments?.getString("name")
val account = UserData.getAccount(accountName)
SingleAccountBody(account = account)
}
}
}

 

이와 같이 하나의 컴포즈를 만든후 이를 인스턴스하면 우리가 했던 모든것을 컴포즈화 해서 나타낼수 있게된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유