๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Android] Jetpack Compose - Navigation ๊ธฐ์ดˆ

interfacer_han 2024. 9. 12. 16:06

Jetpack Compose ๊ฒŒ์‹œ๊ธ€ ์‹œ๋ฆฌ์ฆˆ


#1 ๊ฐœ์š”

#1-1 ์ „ํ†ต์ ์ธ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Navigation

 

[Android] Navigation - ๊ธฐ์ดˆ

#1 ์ด์ „ ๊ธ€ [Android] Navigation - ํ™˜๊ฒฝ ์„ค์ • #1 Navigation#1-1 ์•กํ‹ฐ๋น„ํ‹ฐ ๋ฐ ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ตฌ์„ฑ์˜ ํŠธ๋ Œ๋“œ์š”์ฆ˜ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์˜ ํŠธ๋ Œ๋“œ๋Š” ํ•˜๋‚˜์˜ ์•กํ‹ฐ๋น„ํ‹ฐ, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋‹ค. ์ด๋Š” ๊ตฌ๊ธ€์˜ ๊ถŒ์žฅ์‚ฌํ•ญ

kenel.tistory.com

์œ„ ๊ฒŒ์‹œ๊ธ€์€ Jetpack Compose๊ฐ€ ์“ฐ์ด์ง€ ์•Š์€ ์ „ํ†ต์ ์ธ ๊ตฌ์กฐ์˜ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Navigation์— ๋Œ€ํ•ด ๋‹ค๋ฃฌ ๊ฒŒ์‹œ๊ธ€์ด๋‹ค.

 

#1-2 Jetpack Compose ๊ตฌ์กฐ์˜ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Navigation

 

Compose๋ฅผ ์‚ฌ์šฉํ•œ ํƒ์ƒ‰  |  Jetpack Compose  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Compose๋ฅผ ์‚ฌ์šฉํ•œ ํƒ์ƒ‰ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ํƒ์ƒ‰ ๊ตฌ์„ฑ์š”์†Œ๋Š” Jetpack ์ง€์›์„ ์ œ

developer.android.com

๋ณธ ๊ฒŒ์‹œ๊ธ€์€ Jetpack Compose ๊ตฌ์กฐ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Navigation์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณธ๋‹ค. #1-1๊ณผ ํ•˜๋Š” ๋™์ž‘์€ ๋™์ผํ•˜์ง€๋งŒ, ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ ๊ฝค ๋‹ค๋ฅด๋‹ค.

 

#2 ๊ธฐ์ œ

#2-1 ๊ฐœ์š”

NavHost ๊ฐ์ฒด๊ฐ€ ํ™”๋ฉด ์ „ํ™˜์„ ์ฃผ๊ด€ํ•œ๋‹ค. ํ™”๋ฉด ์ „ํ™˜์„ ํŠธ๋ฆฌ๊ฑฐ(๋ฐœ์ƒ)์‹œํ‚ค๋Š” ๊ฐ์ฒด๋Š” NavHostController๋กœ, ๋งˆ์น˜ TV ๋ฆฌ๋ชจ์ฝ˜๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ํ•œ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. NavHost์— ์˜ํ•ด ๊ด€๋ฆฌ๋  ๊ฐ๊ฐ์˜ ํ™”๋ฉด ์ปดํฌ์ €๋ธ”์„ Destination(๋ชฉ์ ์ง€)์ด๋ผ ํ•˜๋ฉฐ, ๊ทธ Destination์€ route๋ผ๋Š” Stringํ˜• ์ธ์ˆ˜๋ฅผ ๊ฐ€์ง„๋‹ค. ์ด route๋Š” ๊ฐ Destination์„ ์‹๋ณ„(๊ตฌ๋ถ„)ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, ๊ฐ™์€ NavHost ๋‚ด์—์„œ route์˜ ์ด๋ฆ„์€ ์ค‘๋ณต๋จ ์—†์ด ๊ณ ์œ (unique)ํ•ด์•ผ ํ•œ๋‹ค.

 

#2-2 ๋„์‹๋„

์ž์œ  ํ˜•์‹์œผ๋กœ ๊ทธ๋ฆฐ ๋„์‹๋„๋‹ค. #3์—์„œ ์‹ค์ œ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค.

 

#2-3 NavHost

@Composable
public fun NavHost(
    navController: NavHostController,
    startDestination: String,
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    route: String? = null,
    enterTransition: @JvmSuppressWildcards() (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
    exitTransition: @JvmSuppressWildcards() (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
    popEnterTransition: @JvmSuppressWildcards() (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = enterTransition,
    popExitTransition: @JvmSuppressWildcards() (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = exitTransition,
    sizeTransform: @JvmSuppressWildcards() (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null,
    builder: NavGraphBuilder.() -> Unit
): Unit

Navigation ๋™์ž‘์„ ์ฃผ๊ด€ํ•˜๋Š” ๊ฐ์ฒด๋Š” NavHost๋ผ๋Š” @Composable ํ•จ์ˆ˜๋‹ค. ์ด์ „ ๊ฒŒ์‹œ๊ธ€(#1-1)์—์„œ๋Š” Activity ๋˜๋Š” Fragment์˜ ์ „ํ™˜์„ ์œ„ํ•ด Navigation graph๋ผ๋Š” ์ผ์ข…์˜ ์ง€๋„๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค. Jetpack Compose์˜ Navigation์—์„œ Navigation graph์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ NavHost๋‹ค.

 

๊ฐ ์ธ์ˆ˜์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

๋”๋ณด๊ธฐ

navController: NavHostController
NavHost๊ฐ€ ์ง€๋„๋ผ๋ฉด, NavHostController๋Š” ์ˆœ๊ฐ„์ด๋™ ์žฅ์น˜๋‹ค. ์ง€๋„ ์œ„์—์„œ ๊ฐ€๊ณ ์‹ถ์€ ๋ชฉ์ ์ง€(route)๋ฅผ ์„ ํƒํ•˜์—ฌ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ™”๋ฉด ๊ฐ„์˜ ์ „ํ™˜์„ ํŠธ๋ฆฌ๊ฑฐ(๋ฐœ์ƒ)์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” NavHostController๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. TV ๋ฆฌ๋ชจ์ฝ˜ํ•˜๊ณ  ๋น„์Šทํ•˜๋‹ค.

startDestination: String
๋‚ด๋น„๊ฒŒ์ด์…˜ ๊ทธ๋ž˜ํ”„์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ‘œ์‹œ๋  ํ•˜์œ„ ์ปดํฌ์ €๋ธ”์ด๋‹ค. NavHost๋Š” Composable ํ•จ์ˆ˜์ง€๋งŒ, ๋ˆˆ์— ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ์ž๊ธฐ ์ž์‹  ๋Œ€์‹  startDestination์œผ๋กœ ์ง€์ •๋œ ํ•˜์œ„ ์ปดํฌ์ €๋ธ”์„ ํ‘œ์‹œํ•œ๋‹ค.

route: String?
NavHost์˜ ์‹๋ณ„์ž. ํ•˜๋‚˜์˜ NavHost๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ•˜์œ„ ์ปดํฌ์ €๋ธ”์ธ ๋ชฉ์ ์ง€(Destination)์˜ ์‹๋ณ„์ž(route)ํ•˜๊ณ ๋Š” ๋‹ค๋ฅธ ๊ฐœ๋…์ด๋‹ค. ์•ฑ ๋‚ด์—์„œ NavHost๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉ๋˜๋Š” ์ธ์ˆ˜๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ null๋กœ ์ง€์ •๋˜์–ด์žˆ๋‹ค.

 

@JvmSuppressWildcards()

์ž๋ฐ”์™€์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜. ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์ƒ๋žตํ•˜๋ฉด NavHost๋ฅผ ์ž๋ฐ” ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ, ์ธ์ˆ˜ ๋ฐ์ดํ„ฐํ˜•์— "? super" ๋ฐ "? extends" ๋“ฑ์˜ ์™€์ผ๋“œ์นด๋“œ("?") ๊ตฌ๋ฌธ์ด ์ถ”๊ฐ€๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๋”๋Ÿฌ์›Œ์ง„๋‹ค๊ณ  ํ•œ๋‹ค.

enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
๋‹ค๋ฅธ Destination์ด ์ข…๋ฃŒ๋˜๋ฉฐ ํ˜„์žฌ Destination๋กœ ์ „ํ™˜ํ–ˆ์„ ๋•Œ ๋‚˜์˜ค๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜.

(์ฐธ์กฐ: ์ „ํ†ต์ ์ธ ๊ตฌ์กฐ์—์„œ์˜ Navigation ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ enterTransition)

exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
ํ˜„์žฌ Destination์ด ์ข…๋ฃŒ๋  ๋•Œ ๋‹ค๋ฅธ Destination์œผ๋กœ ์ „ํ™˜๋˜๋ฉฐ ๋‚˜์˜ค๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜.

(์ฐธ์กฐ: ์ „ํ†ต์ ์ธ ๊ตฌ์กฐ์—์„œ์˜ Navigation ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ exitTransition)

popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
(์‚ฌ์šฉ์ž์˜ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ์ธํ•ด) ์ด์ „ Destination์ด ์ข…๋ฃŒ๋˜๋ฉฐ ํ˜„์žฌ Destination๋กœ ์ „ํ™˜ํ–ˆ์„ ๋•Œ ๋‚˜์˜ค๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜.

(์ฐธ์กฐ: ์ „ํ†ต์ ์ธ ๊ตฌ์กฐ์—์„œ์˜ Navigation ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ popEnterTransition)

popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition

(์‚ฌ์šฉ์ž์˜ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ์ธํ•ด) ํ˜„์žฌ Destination์ด ์ข…๋ฃŒ๋  ๋•Œ ๋‹ค๋ฅธ Destination์œผ๋กœ ์ „ํ™˜๋˜๋ฉฐ ๋‚˜์˜ค๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜.

(์ฐธ์กฐ: ์ „ํ†ต์ ์ธ ๊ตฌ์กฐ์—์„œ์˜ Navigation ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ popExitTransition)

sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)?
ํ™”๋ฉด ์ „ํ™˜ ์‹œ ํฌ๊ธฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ null์ด๋‹ค.

builder: NavGraphBuilder.() -> Unit
NavHost๊ฐ€ ๊ฐ€์งˆ Destination๋“ค์„ ์ •์˜.

 

#2-4 builder: NavGraphBuilder.() -> Unit

public fun NavGraphBuilder.composable(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    content: @Composable (NavBackStackEntry) -> Unit
): Unit

NavHost์— ์ •์˜๋œ ๋งˆ์ง€๋ง‰ ์ธ์ˆ˜์ด๋ฉฐ, NavHost๊ฐ€ ๋ณด์œ ํ•˜๋Š” Destination์˜ ์ •์ฒด์ด๊ธฐ๋„ ํ•˜๋‹ค.

 

๊ฐ ์ธ์ˆ˜์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

route: String
Destination๋ผ๋ฆฌ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ์ด๋ฆ„.

arguments: List<NamedNavArgument>
Destination ๊ฐ„ ์ „๋‹ฌํ•  ๋ฐ์ดํ„ฐ์˜ ๋ฆฌ์ŠคํŠธ. ํ™”๋ฉด ์ „ํ™˜ ์‹œ์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๋ชฉ์ ์ด๋‹ค. ๋งˆ์น˜ ์›น ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ URL์— ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์–ด๋ณด๋‚ด๋Š” ๊ฒƒ(GET ์š”์ฒญ)๊ณผ ๋น„์Šทํ•˜๊ฒŒ, route์— ์ด arguments๋ฅผ ๋„ฃ์–ด ์‹ค์–ด๋ณด๋‚ธ๋‹ค. ์ด์–ด์ง€๋Š” ๊ธ€(#8)์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃฌ๋‹ค.

deepLinks: List<NavDeepLink>
ํ•ด๋‹น Destination์— ์ง„์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๋”ฅ ๋งํฌ๋“ค์˜ ๋ฆฌ์ŠคํŠธ. ๋”ฅ ๋งํฌ๋Š” ์•ฑ ์™ธ๋ถ€(์›น ๋ธŒ๋ผ์šฐ์ €, QR ์ฝ”๋“œ, ํƒ€ ์•ฑ)์—์„œ ํŠน์ • Destination์œผ๋กœ ๋ฐ”๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋งํฌ๋ฅผ ๋งํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„  ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค.

content: @Composable (NavBackStackEntry) -> Unit
Destination์— ํ•ด๋‹นํ•˜๋Š” ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜. NavBackStackEntry๋Š” Destination์˜ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด๋กœ, arguments์— ์ €์žฅํ•œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ํ†ต๋กœ๋กœ ์‚ฌ์šฉ๋œ๋‹ค (์ด์–ด์ง€๋Š” ๊ธ€(#8) ์ฐธ์กฐ).

 

#3 ๊ตฌํ˜„

#3-1 ๋ชจ๋“ˆ ์ˆ˜์ค€ build.gradle.kt

plugins {
    ...
}

android {
    ...
}

dependencies {

    ...
    
    // Navigation
    implementation("androidx.navigation:navigation-runtime-ktx:2.8.0")
    implementation("androidx.navigation:navigation-compose:2.8.0")
}

 

#3-2 NavHost

// package com.example.navigationbasics

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun MyNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController()
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = "firstScreen"
    ) {
        // (1) FirstScreen
        composable(route = "firstScreen") {
            FirstScreen(navController)
        }

        // (2) SecondScreen
        composable(route = "secondScreen") {
            SecondScreen(navController)
        }
    }
}

MyNavHost๋Š” Activity์˜ setContent  { ... }์— ๋„ฃ์„ ์ปดํฌ์ €๋ธ”์ด๋‹ค (#3-5 ์ฐธ์กฐ). ์œ„ ์ฝ”๋“œ์—์„œ MyNavHost ๋ฐ NavHost์˜ modifier๋ฅผ ํ•˜์œ„ ์ปดํฌ์ €๋ธ”์ธ FirstScreen ๋ฐ SecondScreen์— ์ „๋‹ฌํ•˜๊ณ  ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ, ๊ทธ ์กด์žฌ๊ฐ€ ๋ฌด์˜๋ฏธํ•˜๋‹ค. ์‚ญ์ œํ•˜๋Š” ํŽธ์ด ๋” ๊น”๋”ํ•œ ์ฝ”๋“œ๊ฒ ์ง€๋งŒ, Activity๋กœ๋ถ€ํ„ฐ Modifier๋ฅผ ์ด์–ด๋ฐ›์•„ NavHost๋ฅผ ๊ฑฐ์ณ Destination๊นŒ์ง€ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฑธ ๋ณด์ด๊ณ  ์‹ถ๊ธฐ์— ๊ทธ๋ƒฅ ๋ƒ…๋‘๊ฒ ๋‹ค.

 

#3-3 ์ฒซ๋ฒˆ์งธ Destination (FirstScreen)

// package com.example.navigationbasics

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController

@Composable
fun FirstScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(text = "First Screen")
        Button(
            onClick = { navController.navigate("secondScreen") }
        ) {
            Text(text = "Go to Second Screen")
        }
    }
}

NavHostController๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

#3-4 ๋‘๋ฒˆ์งธ Destination (SecondScreen)

// package com.example.navigationbasics

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController

@Composable
fun SecondScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(text = "Second Screen")
        Button(
            onClick = { navController.navigate("firstScreen") }
        ) {
            Text(text = "Go to First Screen")
        }
    }
}

#3-3๊ณผ ๊ฐ™์€ ๋งฅ๋ฝ์˜ ์ฝ”๋“œ๋‹ค.

 

#3-5 Activity

// package com.example.navigationbasics

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import com.example.navigationbasics.ui.theme.NavigationBasicsTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            NavigationBasicsTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    MyNavHost(modifier = Modifier.padding(innerPadding))
                }
            }
        }
    }
}

MyNavHost๋Š” ์ปดํฌ์ €๋ธ”์ด์ง€๋งŒ ํ˜•ํƒœ๊ฐ€ ์—†๋‹ค. ๋Œ€์‹ , NavHost()์˜ ์ธ์ˆ˜ ์ค‘ ํ•˜๋‚˜์ธ startDestination์— ์ ํžŒ Destination์„ ๋Œ€์‹  ๋ณด์—ฌ์ค€๋‹ค.

 

#4 ์ž‘๋™ ํ™•์ธ

 

#5 ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง: ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์›(single source of truth, SSOT) ์›์น™

#5-1 SSOT ์›์น™?

 

๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์› - ์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „

์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „.

ko.wikipedia.org

์œ„ ์›์น™์„ ํ•œ ๋งˆ๋””๋กœ ์ •์˜ํ•˜๋ฉด, "๋ฐ์ดํ„ฐ ์กฐ์ž‘ ๋™์ž‘์„ ํ•œ ๊ณณ์œผ๋กœ ๋ชฝ๋•… ๋ชจ์Œ"์ด๋‹ค. ์ด ์›์น™์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ #3์˜ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•ด๋ณธ๋‹ค.

 

#5-2 NavHost

// package com.example.navigationbasics

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable

import androidx.navigation.compose.rememberNavController

@Composable
fun MyNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController()
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = "firstScreen"
    ) {
        // (1) FirstScreen
        composable(route = "firstScreen") {
            FirstScreen(navigateToSecondScreen = { navController.navigate("secondScreen") })
        }

        // (2) SecondScreen
        composable(route = "secondScreen") {
            SecondScreen(navigateToFirstScreen = { navController.navigate("firstScreen") })
        }
    }
}

FirstScreen ๋ฐ SecondScreen์— NavHostController๋ฅผ ์ฃผ๋Š” ๋Œ€์‹ , NavHostController์˜ ํ•œ ๋™์ž‘(์ด๋ฒคํŠธ)๋ฅผ ์ฃผ๊ฒŒ๋” ๋ฐ”๊พผ๋‹ค. ์ฆ‰, FirstScreen์—์„œ ์กฐ์ž‘ํ•  NavHostController์˜ ๋™์ž‘์„ ๋ฏธ๋ฆฌ NavHost๋‹จ์—์„œ ์ •์˜ํ•˜์—ฌ ๋‚ด๋ ค์ค€๋‹ค. ์ด๋ฅผ SSOT ํŒจํ„ด์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ด ํŒจํ„ด์„ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ (๊ธฐ์กด ๋Œ€๋น„ ๋ณ€์ˆ˜๋ฅผ ๋ณด๋‚ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์—์„œ) ๊ฐ์ฒด ๊ฐ„ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์กŒ๋‹ค.

 

#5-3 FirstScreen

// package com.example.navigationbasics

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@Composable
fun FirstScreen(navigateToSecondScreen: () -> Unit) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(text = "First Screen")
        Button(
            onClick = { navigateToSecondScreen() }
        ) {
            Text(text = "Go to Second Screen")
        }
    }
}

Button์˜ onClick ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•œ๋‹ค. SecondScreen๋„ ์ด์™€ ๊ฐ™์€ ๋งฅ๋ฝ์œผ๋กœ ๊ณ ์น  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ƒ๋žตํ•œ๋‹ค.

 

#6 ์š”์•ฝ

NavHost๋Š” TV ์ฑ„๋„ ๋ชฉ๋ก, NavController๋Š” TV ๋ฆฌ๋ชจ์ฝ˜์ด๋‹ค.

 

#7 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/jetpack-compose/NavigationBasics at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com

 

#8 ์ด์–ด์ง€๋Š” ๊ธ€

#8-1 Destination ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (NavBackStackEntry.arguments)

 

[Android] Jetpack Compose - Navigation์˜ Destination ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (NavBackStackEntry.arguments)

#1 ์ด์ „ ๊ธ€ [Android] Jetpack Compose - Navigation ๊ธฐ์ดˆ#1 ๊ฐœ์š”#1-1 ์ „ํ†ต์ ์ธ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Navigation [Android] Navigation - ๊ธฐ์ดˆ#1 ์ด์ „ ๊ธ€ [Android] Navigation - ํ™˜๊ฒฝ ์„ค์ • #1 Navigation#1-1 ์•กํ‹ฐ๋น„ํ‹ฐ ๋ฐ ํ”„

kenel.tistory.com

 

#8-2 NavigationBar

[๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ๋ฐ ๋งํฌ ์ถ”๊ฐ€ ์˜ˆ์ •]