[Android][Compose] Compose에서 네비게이션 사용하기

Intelli·2023년 5월 17일
4
post-thumbnail

0. Setup

dependency 추가하기

dependencies {
    def nav_version = "2.5.3"

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

1. route정의 & NavHostController 만들기

1-1. destination을 위해 route 정의하기.

  1. enum class에 route 정의
enum class DotoringLoginScreen() {
	Branch,
    Login,
    Register
}

1-2. NavController 만들기

  1. NavController란?
    NavController는 back stack과 각 스크린의 상태를 추적합니다.

  2. NavController 생성

val navController = rememberNavController()
  1. NavController의 생성 위치
    NavController는 NavController를 참조하는 모든 Composable들이 접근할 수 있는 위치에 정의해야 한다. 이를 통해 State Hoisting과 Source of Truth 원칙을 지킬 수 있다. 왜냐하면 Navigation과 관련된 상태 정보를 스크린과 분리하며, NavController가 생성된 곳에 있는 상태 정보를 여러 스크린이 공유할 수 있기 때문이다.

2. NavHost 추가하기

2-1. NavHost란?

NavController는 하나의 NavHost와 연관되어 있어야 한다. NavHost는 NavController와 Navigation Graph를 연결하는 역할을 한다. 화면 전환을 하면서 NavHost 안의 내용은 자동적으로 recompose된다.

2-2. NavHost의 syntax

	NavHost(
    	navController,
        startDestination,
        modifier,
    ) {
    	content
    }
  • startDestination: 앱이 처음 NavHost로 보여주는 화면을 가리키는 route이다.
  • content: NavController의 destination에 해당하는 모든 composable을 정의한다.

2-3. composable의 syntax

NavHost의 content안에 다음의 내용을 작성한다.

composable(route) {
	content
}
  • route: route 이름을 나타내는 String. enum class에 정의해둔 내용을 활용한다.
  • content: 주어진 route에서 display하고자 하는 composable을 호출할 수 있다.

2-4. NavHost 예시

	NavHost(
    	navController = navController,
        startDestination = DotoringLoginScreen.Branch.name,
        modifier = modifier,
    ) {
    	composable(DotoringLoginScreen.Branch.name) { /*스크린 호출*/ }
        composable(DotoringLoginScreen.Login.name) { /*스크린 호출*/ }
        composable(DotoringLoginScreen.Registser.name) { /*스크린 호출*/ }
    }

3. Navigate하기

3-1. navigate()메소드

  1. 화면 전환 하기
    navigate(route)를 통해 destination을 인자로 주어 원하는 화면으로 전환이 가능하다. 이 때, 화면 전환과 함께 back stack에 전환 전의 화면이 추가된다. 다음과 같이 사용한다.
navController.navigate(DotoringLoginScreen.Login.name)
  1. navigate()메소드의 옵션들
    다양한 옵션들을 이용하여 back stack을 관리할 수 있다.
  • popUpTo(route)
    navigate에 전달한 인자에 해당하는 화면으로 전환하기 전에, route에 해당하는 화면이 나오기 전까지의 back stack을 뺀다. inclusive를 true로 하면 route에 해당하는 화면까지 back stack에서 뺀다.
  • launchSingleTop = true
    route에 해당하는 화면이 아닌 경우에만 route로 화면이 전환된다. back stack에 동일한 route가 중복으로 있는 것을 방지한다.
  1. navigate() 메소드를 어디에 작성해야 하는가
    Single source of truth 원칙을 지키기 위해서, NavController를 hoist한 composable과 매개변수로 받는 composable이 navigation을 호출해야 한다. 하위 composable에서 발생한 navigation이벤트는 해당 이벤트를 호출자에게 노출해야 한다.

  2. navigate() 메소드의 사용

@Composable
fun NavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = DotoringLoginScreen.Branch.name
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable(DotoringBranchScreen.Branch.name) {
            BranchScreen(
                onNavigateToLogin = { navController.navigate(DotoringLoginScreen.Login.name) },
                /*...*/
            )
        }
        
        composable(DotoringLoginScreen.Login.name) {
            BranchScreen(
            	/*...*/
            )
        }
        
        composable(DotoringRegisterScreen.Register.name) {
            RegisterScreen(
                /*...*/
            )
        }
    }
}

@Composable
fun BranchScreen(
    onNavigateToLogin: () -> Unit,
    /*...*/
) {
    /*...*/
    Button(onClick = onNavigateToLogin) {
        Text(text = "로그인 화면")
    }
}

1️⃣ 네비게이션의 구현

  • 다른 스크린 컴포저블에서 CupcakeApp에 있는 NavHostController에 접근해야 한다.
    • navController를 각 스크린의 매개변수로 넘겨준다?
      • Navigation Logic은 각 스크린에 네비게이션 접근 권한을 주면 안되고, 한 곳에 있어야한다.
    • 유저가 버튼을 클릭했을 때 어떤 일이 일어나는지 NavHost안에 선언된 Composable에 람다 함수로 넘겨준다.
      • 네비게이션 로직이 스크린에 노출되지 않고, NavHost에서만 네비게이션을 핸들링할 수 있다.

2️⃣ 버튼 핸들러 추가하기

  1. 스크린에 버튼 핸들러를 매개변수로 추가함.
  2. 인자로 받은 핸들러를 버튼의 onClick 인자로 초기화.

3️⃣ 다른 route로 navigate 하기

  1. NavHostController 객체에서 navigate() 메소드 호출
navController.navigate(route)
  • route가 NavHost안 composable()에 작성한 route와 같으면 앱이 해당 스크린으로 navigate한다.
  • 각 스크린에서 버튼을 눌렀을 때, navigate() 메소드가 호출되도록 한다.
    • onNextButtonClicked의 인자로 넘겨주기.
  1. navigate()함수 호출의 결과
  • 스크린을 destination으로 변경
  • backstack에 이전 스크린 추가

4️⃣ popBackStack()메소드의 사용

  1. popBackStack()메소드는 언제 사용할까?
  • 여러 작업들을 취소하고 싶을 때.
  1. popBackStack() 메소드 syntax
navController.popBackStack(route, inclusive)
  • route: 돌아가고 싶은 화면의 route
  • inclusive: Boolean값. true이면 특정한 route를 삭제, false이면 모든 destination을 pop하고 start destination으로 이동.
  1. 활용
  • Cancel버튼의 onClick인자로 넘김

3. 다른 앱으로 이동하기

1️⃣ 어떻게 다른 앱으로 이동할 수 있을까?

  • ShareSheet을 이용한다.
    • ShareSheet은 앱의 UI가 아니라 AOS에서 제공되는 UI이다.
    • navContoller가 아니라 intent를 이용해야 System UI를 이용할 수 있다.
    • ACTION_SEND 인텐트로 데이터 sharing action을 만들어 낼 수 있다.

2️⃣ intent 설정 코드

  1. intent 객체 생성 & intent 종류 명시
  2. intent와 함께 보내는 데이터 타입 명시
  3. intent에 데이터 전달, putExtra()메소드 사용
  4. intent로 생성된 activity를 startActivity() 메소드에 전달

4. Navigation에 반응하는 앱바 만들기

1️⃣ 원하는 기능과 그 구현

  1. 타이틀 자동 업데이트
  • AppBar 컴포저블은 현재 스크린을 알아야 한다.
  1. NavController에 BackStack이 있을 때만 Up button 보임
  • backstack을 참조해야 한다.

5. +@

  • Scaffold
    • material design layout.
    • 몇몇 material component를 조합할 API를 제공한다.
  • Context
    • 안드로이드 시스템에서 구현이 제공되는 추상 클래스
    • application-specific 리소스와 클래스에 접근하도록 한다.
  • Pair
    • 두 제네릭 값의 쌍을 나타낸다.

Reference

AndroidStudio | Navigate between screens with Compose

profile
I never dreamed about success. I worked for it.

5개의 댓글

comment-user-thumbnail
2023년 5월 17일

잘 보고 가요~!👍

1개의 답글
comment-user-thumbnail
2023년 5월 22일

깔끔하고 이해하기 쉽게 작성해주셔서 너무 좋아요!!

답글 달기
comment-user-thumbnail
2023년 5월 22일

잘 보고 갑니다!

답글 달기
comment-user-thumbnail
2023년 5월 22일

내비게이션 작성위치가 정말 중요한 것 같아요~

답글 달기