$30 off During Our Annual Pro Sale. View Details »

Navigation 3: 적응형 UI를 위한 앱 탐색

Avatar for Sungyong An Sungyong An
December 06, 2025

Navigation 3: 적응형 UI를 위한 앱 탐색

2025년 12월 06일 (토) DEVFEST INCHEON 2025 행사에서 발표한 내용입니다.
Navigation3 라이브러리를 살펴보고, 적응형 UI 환경에서의 구현 이점과 마이그레이션할 때 주의할 점을 설명합니다.

https://www.ticketa.co/events/40

Avatar for Sungyong An

Sungyong An

December 06, 2025
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. INCHEON ఐ࢝(Navigation)਷ জ ղ੄ ׮নೠ ௑బஎܳ о۽૕۞ ੉زೞѢա, Ӓ উਵ۽

    ٜযоҊ, ׮द աৢ ࣻ ੓ѱ ೞח ࢚ഐ੘ਊਸ ੄޷೤פ׮. য়ט ࢓ಝࠅ ઱ઁੑפ׮.
  2. History 2008 2011 2019 2021 2025 Multiple Activities Android 1.0

    Link: h tt ps://android-developers.googleblog.com/2008/09/announcing-android-10-sdk-release-1.html উ٘۽੉٘ OSח Activityۄח ҳࢿ ਃࣗо ചݶ ೞաܳ աఋղח Ѫਵ۽ द੘غ঻णפ׮.
  3. Multiple Activities • 1 Activity = 1 screen • Intent

    — Extra Activityח Intentܳ दझమী ੹׳ೞҊ, दझమ੉ ׮਺ Activityܳ प೯೤פ׮.
  4. val intent = Intent(this, MovieActivity::class.java) .putExtra("id", 1234) startActivity(intent) class MovieActivity

    : Activity() { override fun onCreate(savedInstanceState: Bundle?) { val id = intent.getIntExtra("id", 0) } } Activityח Intentܳ दझమী ੹׳ೞҊ, दझమ੉ ׮਺ Activityܳ प೯೤פ׮.
  5. Multiple Activities • Back stack (system ࣗਬ) • Task →

    launchMode, Intent flags … 🤔 Back stack਷ Taskۄח ױਤ۽ ޘৈࢲ ҙܻغҊ, दझమ ࣗਬੑפ׮.
  6. History 2008 2011 2019 2021 2025 Multiple Activities Multiple Fragments

    Honeycomb ( Tablet ) Android 1.0 Link: h tt ps://android-developers.googleblog.com/2011/02/android-30-fragments-api.html Android Tablet੉ ١੢ೞݶࢲ Fragmentۄח ѐ֛੉ ࢤ҂Ҋ,
  7. Multiple Fragments • 1 Fragment = 1 screen • Transaction

    — Argument ೞա੄ ചݶਸ Activity ؀न Fragment۽ ҳࢿೞ۰ח জب ੼੼ ࢤ҂णפ׮.
  8. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    supportFragmentManager.commit { setReorderingAllowed(true) add<MovieFragment>(R.id.fragment_container_view, args = bundleOf("id" to 1234)) } } } class MovieFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val id = requireArguments().getInt("id") } } Fragmentח DB୊ۢ Transactionਵ۽ ׮਺ Fragmentܳ ਃ୒೤פ׮.
  9. Multiple Fragments • Back stack (FragmentManager ࣗਬ) • Fragment hierarchy

    😵💫 Link: h tt ps://developer.android.com/guide/fragments/fragmentmanager दझమ੄ ࠂ੟ೠ Task ز੘ীࢲ ߩযլ૑݅, Fragmentܳ ҙܻೞח Ѫب рױ஖ ঋওणפ׮.
  10. History 2008 2011 2019 2021 2025 Multiple Activities Multiple Fragments

    Honeycomb ( Tablet ) Android 1.0 Jetpack Navigation Link: h tt ps://android-developers.googleblog.com/2019/03/android-jetpack-navigation-stable.html Ӓېࢲ Jetpack Navigation੉ ١੢೮णפ׮.
  11. Jetpack Navigation • 1 Fragment = 1 screen • NavDirections

    — NavArgs NavHost (NavHostFragment) NavController NavGraph NavigatorProvider Navigator NavBackStackEntry NavDestination ‘B’ NavDestination ‘A’ NavDirections NavArgs زੌೞѱ Multiple Fragment ҳઑ૑݅, ۄ੉࠳۞ܻ ղࠗীࢲ Transactionਸ ҙܻ೤פ׮.
  12. class MovieListFragment : Fragment() { ... val navController = findNavController()

    navController.navigate( MovieListFragmentDirections.actionToMovie(id = 1234) ) } class MovieFragment : Fragment() { private val args: MovieFragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val id = args.id } } Jetpack Navigationীࢲח navigate() API۽ ׮਺ Fragmentܳ ਃ୒೤פ׮.
  13. res/navigation/nav_sample.xml <navigation android:id="@+id/nav_graph" app:startDestination="@id/list"> <fragment android:id="@+id/list" android:name="com.example.ListFragment"> <action android:id="@+id/action_to_detail" app:destination="@id/detail"

    /> </fragment> <fragment android:id="@+id/detail" android:name="com.example.DetailFragment"> <argument android:name="id" app:argType="integer" /> </fragment> </navigation> ӒܻҊ Layout XML୊ۢ Navigation XML۽ Graphܳ ੿੄ೞݶ ؾפ׮.
  14. Jetpack Navigation • Back stack (NavController ࣗਬ) • Navigation Graph

    XML 🧐 NavHost (NavHostFragment) NavController NavGraph NavigatorProvider Navigator NavBackStackEntry NavDestination ‘B’ NavDestination ‘A’ NavDirections NavArgs Back Stack਷ ۄ੉࠳۞ܻ ղࠗীࢲ ੗زਵ۽ ҙܻؾפ׮.
  15. History 2008 2011 2019 2021 2025 Multiple Activities Multiple Fragments

    navigation- compose Honeycomb ( Tablet ) Android 1.0 Compose 1.0 Jetpack Navigation Link: h tt ps://android-developers.googleblog.com/2021/07/jetpack-compose-announcement.html Ӓؘ۠ Composeо ١੢೮णפ׮.
  16. Navigation Compose • 1 Composable = 1 screen • Route

    (ӝઓীח ID ӝ߈) NavHost NavController NavGraph NavigatorProvider Navigator NavBackStackEntry NavDestination ‘B’ NavDestination ‘A’ Route Route ചݶ ೞաܳ Composable۽ ҳࢿೡ ࣻ ੓ѱ, ӝઓ Jetpack Navigation ӝ߈ী Compose ӝמਸ ୶о೮णפ׮.
  17. ~ navigation 2.7.0 val navController = rememberNavController() NavHost(navController = navController,

    startDestination = "movieList") { composable("movieList") { MovieListScreen(onItemClick = { navController.navigate(route = "movie/1234")) }) } composable("movie/{id}", arguments = listOf(navArgument("myId") { type = NavType.IntType }) ) { backStackEntry -> val id = backStackEntry.arguments?.getInt("id") MovieScreen(id) } } navigate() API۽ ׮਺ Composableਸ ਃ୒೤פ׮.
  18. @Serializable object MovieList @Serializable class Movie(val id: Int) val navController

    = rememberNavController() NavHost(navController = navController, startDestination = MovieList) { composable<MovieList> { MovieListScreen(onItemClick = { navController.navigate(route = Movie(id = 1234)) }) } composable<Movie> { backStackEntry -> val movie: Movie = backStackEntry.toRoute() MovieScreen(chat.id) } } navigate() API۽ ׮਺ Composableਸ ਃ୒೤פ׮.
  19. Navigation Compose • Back stack (NavController ࣗਬ) • ӝઓ Fragment

    ߑधਸ Compose۽ ੉ध (Graph ӝ߈) • ׮নೠ ޙઁٜ… 👾 Content Content State State NavController Backstack NavHost Content State Event Fragment੄ ݺ۸ഋ ߑध੉ Composeীࢲח ׮নೠ ޙઁܳ ٜ݅঻णפ׮.
  20. val backStack by remember { mutableStateListOf("movieList") } BackHandler(backStack.size > 1)

    { backStack.removeLastOrNull() } AnimatedContent(targetState = backStack.lastOrNull()) { screen -> if (screen == "movieList") { MovieListScreen(onItemClick = { backStack += "movie/$it" }) } else if (screen.startsWith("movie/")) { MovieScreen(screen.substring(screen.indexOf('/')).toInt()) } } Back stackਸ рױೠ SnapshotStateList۽ ੿੄ೞҊ
  21. val backStack by remember { mutableStateListOf("movieList") } BackHandler(backStack.size > 1)

    { backStack.removeLastOrNull() } AnimatedContent(targetState = backStack.lastOrNull()) { screen -> if (screen == "movieList") { MovieListScreen(onItemClick = { backStack += "movie/$it" }) } else if (screen.startsWith("movie/")) { MovieScreen(screen.substring(screen.indexOf('/')).toInt()) } } AnimatedContent৬ Ѿ೤ೞৈ ఐ࢝ਸ ҳഅೡ ࣻ ੓णפ׮.
  22. val backStack by remember { mutableStateListOf("movieList") } BackHandler(backStack.size > 1)

    { backStack.removeLastOrNull() } AnimatedContent(targetState = backStack.lastOrNull()) { screen -> if (screen == "movieList") { MovieListScreen(onItemClick = { backStack += "movie/$it" }) } else if (screen.startsWith("movie/")) { MovieScreen(screen.substring(screen.indexOf('/')).toInt()) } } AnimatedContent৬ Ѿ೤ೞৈ ఐ࢝ਸ ҳഅೡ ࣻ ੓णפ׮.
  23. val backStack by remember { mutableStateListOf("movieList") } BackHandler(backStack.size > 1)

    { backStack.removeLastOrNull() } AnimatedContent(targetState = backStack.lastOrNull()) { screen -> if (screen == "movieList") { MovieListScreen(onItemClick = { backStack += "movie/$it" }) } else if (screen.startsWith("movie/")) { MovieScreen(screen.substring(screen.indexOf('/')).toInt()) } } Back ੉߮౟ب рױ൤ ୊ܻೡ ࣻ ੓णפ׮.
  24. Jetpack Navigation • Owners Lifecycle SavedState ViewModel Navigation Component View

    Fragment Compose ೞ૑݅ Jetpack Navigationח п ചݶ݃׮ ઁҕೞח Ownerо ੓যࢲ, ׮ܲ Jetpack ۄ੉࠳۞ܻ৬ Ѿ೤ೞ۰ݶ ҳഅ੉ ࠂ੟೤פ׮.
  25. History 2008 2011 2019 2021 2025 Multiple Activities Navigation 3

    Multiple Fragments navigation- compose Honeycomb ( Tablet ) Android 1.0 Compose 1.0 Jetpack Navigation Link: h tt ps://android-developers.googleblog.com/2025/11/jetpack-navigation-3-is-stable.html ӒܻҊ Navigation 3о ա৳णפ׮.
  26. Navigation 3 • State ӝ߈ • Back stack (૒੽ ࣗਬ)

    Compose State Content Navigation 3 State in UI out Compose੄ ࢚క ӝ߈ਵ۽ ࢸ҅غয, ੉ઁ Back stackਸ ૒੽ ࣗਬೞѱ غ঻णפ׮.
  27. Back stack Key Content NavEntry Entry provider { key ->

    when (key) { is MovieList -> { NavEntry(key) { MovieListScreen() } } } } Key NavEntry NavEntryܳ ࢤࢿೞৈ ઁҕ೤פ׮.
  28. Back stack Key Content NavEntry Entry provider Key NavEntry NavDisplay

    Owned by You - any type You - Compose type You - Nav3 type Nav3 NavDisplayח Back stackҗ Entry provider۽ ۨ੉ইਓਸ ҳࢿ೤פ׮.
  29. val backStack = rememberNavBackStack(MovieList) NavDisplay(backStack = backStack) { key ->

    when (key) { is MovieList -> NavEntry(key) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } is Movie -> NavEntry(key) { MovieScreen(key.id) } else -> error() } } Entry providerীࢲח ߉਷ Key৬ ݒடغח NavEntryܳ ࢤࢿ೤פ׮.
  30. val backStack = rememberNavBackStack(MovieList) NavDisplay(backStack = backStack, entryProvider = entryProvider

    { entry<MovieList> { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie> { movie -> MovieScreen(movie.id) } } ) Entry providerীࢲח ߉਷ Key৬ ݒடغח NavEntryܳ ࢤࢿ೤פ׮.
  31. NavKey val backStack = rememberNavBackStack(MovieList) // push backStack += Movie(it)

    @Serializable data class Movie(val id: Int) : NavKey NavKeyܳ ࢚ࣘ೧ঠ ೤פ׮.
  32. NavKey // NOT saveable val backStack = remember { mutableStateListOf<Any>(MovieList)

    } // push backStack += Movie(it) @Serializable data class Movie(val id: Int) : NavKey NavKeyܳ ࢚ࣘೞ૑ ঋҊ ױࣽೠ ݾ۾ਵ۽ب оמೞ૑݅, Config Changesীࢲ ࢚కо ਬपؾפ׮.
  33. // default implementation val backStack = rememberNavBackStack(MovieList) // push backStack

    += Movie(it) @Serializable data class Movie(val id: Int) : NavKey NavKey оә੸ ӝࠄ ҳഅਸ ࢎਊೞח Ѫ੉ ಞܻ೤פ׮.
  34. NavKey - NavEntry val backStack = rememberNavBackStack(MovieList) // push backStack

    += Movie(it) NavDisplay( entryProvider = entryProvider { entry<Movie> { movie -> MovieScreen(movie.id) } } ) Key Content NavEntry Key৬ NavEntryח ࢲ۽ ݒடػ׮Ҋ ೮חؘਃ.
  35. NavEntry.kt @Immutable public class NavEntry<T : Any>( private val key:

    T, public val contentKey: Any = defaultContentKey(key), public val metadata: Map<String, Any> = emptyMap(), private val content: @Composable (T) -> Unit, ) { ... } internal fun defaultContentKey(key: Any): Any = key.toString() Key৬ NavEntryח ࢲ۽ ݒடػ׮Ҋ ೮חؘਃ.
  36. NavEntry.kt @Immutable public class NavEntry<T : Any>( private val key:

    T, public val contentKey: Any = defaultContentKey(key), public val metadata: Map<String, Any> = emptyMap(), private val content: @Composable (T) -> Unit, ) { ... } internal fun defaultContentKey(key: Any): Any = key.toString() key ৻ী contentKeyب ੓णפ׮.
  37. NavEntry.kt @Immutable public class NavEntry<T : Any>( private val key:

    T, public val contentKey: Any = defaultContentKey(key), public val metadata: Map<String, Any> = emptyMap(), private val content: @Composable (T) -> Unit, ) { ... } internal fun defaultContentKey(key: Any): Any = key.toString() ӝࠄ਷ key.toString() чਵ۽
  38. val backStack = rememberNavBackStack(MovieList) @Serializable data object MovieList : NavKey

    @Serializable data class Movie(val id: Int) : NavKey data ௿ېझ ৈࠗী ٮۄ contentKey ч੉ ׳ۄ૘פ׮.
  39. NavDisplay.kt @Composable public fun <T : Any> NavDisplay( entryDecorators: List<NavEntryDecorator<T>>

    = listOf(rememberSaveableStateHolderNavEntryDecorator()), ... ) { ... Content NavEntryDecorator NavEntryDecorator NavEntryDecorator NavEntryDecorator NavEntryDecoratorח ݽٚ NavEntry ஶబஎী ҕాػ ୊ܻܳ ೡ ࣻ ੓חؘਃ.
  40. NavEntryDecorator.kt @Immutable public open class NavEntryDecorator<T : Any>( internal val

    onPop: (key: Any) -> Unit = {}, internal val decorate: @Composable (entry: NavEntry<T>) -> Unit, ) (1) decorate (2) decorate (3) decorate NavEntryDecorator NavEntryDecorator NavEntryDecorator ݾ۾ ࣽࢲী ٮۄ decorateо ࣽର੸ਵ۽ ୊ܻؾפ׮.
  41. NavEntryDecorator.kt @Immutable public open class NavEntryDecorator<T : Any>( internal val

    onPop: (key: Any) -> Unit = {}, internal val decorate: @Composable (entry: NavEntry<T>) -> Unit, ) onPop (6) onPop (5) onPop (4) (1) decorate (2) decorate (3) decorate NavEntryDecorator NavEntryDecorator NavEntryDecorator ݾ۾ ࣽࢲী ٮۄ onPop੉ ࣽର੸ਵ۽ ୊ܻؾפ׮.
  42. NavDisplay.kt @Composable public fun <T : Any> NavDisplay( entryDecorators: List<NavEntryDecorator<T>>

    = listOf(rememberSaveableStateHolderNavEntryDecorator()), ... ) { ... ӝࠄਵ۽ SaveableStateHolder ୊ܻо ੓Ҋ
  43. SaveableStateHolderNavEntryDecorator.kt public class SaveableStateHolderNavEntryDecorator<T : Any>( saveableStateHolder: SaveableStateHolder ) :

    NavEntryDecorator<T>( onPop = { contentKey -> saveableStateHolder.removeState(contentKey) }, decorate = { entry -> saveableStateHolder.SaveableStateProvider(entry.contentKey) { entry.Content() } }, ) ղࠗ੸ਵ۽ contentKeyܳ ӝ߈ਵ۽ ز੘೤פ׮.
  44. SaveableStateHolderNavEntryDecorator.kt public class SaveableStateHolderNavEntryDecorator<T : Any>( saveableStateHolder: SaveableStateHolder ) :

    NavEntryDecorator<T>( onPop = { contentKey -> saveableStateHolder.removeState(contentKey) }, decorate = { entry -> saveableStateHolder.SaveableStateProvider(entry.contentKey) { entry.Content() } }, ) ղࠗ੸ਵ۽ contentKeyܳ ӝ߈ਵ۽ ز੘೤פ׮.
  45. SceneState.kt @Composable public fun <T : Any> rememberSceneState(...): SceneState<T> {

    ... val decoratedEntries = rememberDecoratedNavEntries( entries, listOf( rememberSceneSetupNavEntryDecorator(), rememberBackStackAwareLifecycleNavEntryDecorator(entries), ), ) ... } ੉৻ীب ղࠗ੸ਵ۽ NavEntryDecoratorܳ ӝࠄ ࢸ੿ೞҊ ੓णפ׮.
  46. val backStack = rememberNavBackStack(MovieList) // [MovieList, Movie(1234)] // pop backStack.removeLastOrNull()

    // remove Movie(1234) Back stackীࢲ ݃૑݄ ೦ݾਸ popೞח ௏٘ੑפ׮.
  47. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, onBack =

    { backStack.removeLastOrNull() } ... ) Predictive Back NavDisplayীࢲ onBack੉ ઁҕغחؘਃ.
  48. NavDisplay.kt @Composable public fun <T : Any> NavDisplay(...) { ...

    NavigationBackHandler( // Supports gesture navigation state = gestureState, isBackEnabled = scene.previousEntries.isNotEmpty(), onBackCompleted = { repeat(entries.size - scene.previousEntries.size) { onBack() } }, ) } ղࠗীࢲ Back Gestureܳ ׮ܖҊ ੓णפ׮.
  49. NavDisplay.kt @Composable public fun <T : Any> NavDisplay(...) { ...

    NavigationBackHandler( // Supports gesture navigation state = gestureState, isBackEnabled = scene.previousEntries.isNotEmpty(), onBackCompleted = { repeat(entries.size - scene.previousEntries.size) { onBack() } }, ) } ղࠗীࢲ Back Gestureܳ ׮ܖҊ ੓णפ׮.
  50. NavDisplay( transitionSpec = { ... }, popTransitionSpec = { ...

    }, predictivePopTransitionSpec = { ... }, ) Global Animation ߸҃ೡ ࣻ ੓ח APIо ੓णפ׮.
  51. NavDisplay( transitionSpec = { materialSharedAxisZ(forward = true) }, popTransitionSpec =

    { materialSharedAxisZ(forward = false) }, predictivePopTransitionSpec = { materialSharedAxisZ(forward = false) }, ) (feat. Material Motion) Link: h tt ps://github.com/fornewid/material-motion-compose
  52. NavDisplay( entryProvider = entryProvider { entry<Chat>( metadata = NavDisplay.transitionSpec {

    ... } + NavDisplay.popTransitionSpec { ... } + NavDisplay.predictivePopTransitionSpec { ... } ) { chat -> ChatScreen(chat.id) } } ) Local Animation ౠ੿ NavEntry݅ ߸҃ೡ ࣻ ੓ח APIب ੓णפ׮.
  53. NavEntry.kt @Immutable public class NavEntry<T : Any>( private val key:

    T, public val contentKey: Any = defaultContentKey(key), public val metadata: Map<String, Any> = emptyMap(), private val content: @Composable (T) -> Unit, ) { ... } metadataח Map ഋక۽ ׮নೠ Ҕী ੿ࠁܳ ੹׳೤פ׮.
  54. NavDisplay.kt @Immutable public object NavDisplay { public fun transitionSpec( transitionSpec:

    AnimatedContentTransitionScope<Scene<*>>.() -> ContentTransform? ): Map<String, Any> = mapOf(TRANSITION_SPEC to transitionSpec) ... internal const val TRANSITION_SPEC = "transitionSpec" } ৈӝࢲח ੿੄ػ transitionSpecਸ
  55. NavDisplay.kt @Composable public fun <T : Any> NavDisplay(...) { ...

    val contentTransform = { when { inPredictiveBack -> { ... } isPop -> { ... } else -> { transitionScene.contentTransform(TRANSITION_SPEC)?.invoke(this) ?: transitionSpec(this) } } } ... NavDisplayী ੹׳೤פ׮.
  56. ఃਕ٘ ੿ܻ • NavKey — rememberNavBackstack() • NavEntry — key,

    contentKey, metadata • NavEntryDecorator — decorate, onPop • NavDisplay — transitionSpec • ӒܻҊ “Scene” ݃૑݄ਵ۽ Scene੉ ੓णפ׮.
  57. Content A Content B Content C Scene ೞա੄ ചݶ਷ Sceneਵ۽,

    ӝࠄ਷ ೞա੄ Sceneী ೞա੄ NavEntry ஶబஎܳ ಴द೤פ׮.
  58. SceneStrategy ListDetailScene HorizontalPagerScene DialogScene A B A B C A

    B NavEntry B NavEntry A NavEntry B NavEntry A ? NavEntry C NavEntry B NavEntry A Scene Layout NavDisplay SceneStrategyী ٮۄ, Scene ೞաী ৈ۞ ѐ੄ NavEntryܳ ಴दೡ ࣻب ੓णפ׮.
  59. NavDisplay.kt @Composable public fun <T : Any> NavDisplay( sceneStrategy: SceneStrategy<T>

    = SinglePaneSceneStrategy(), ... ) { val sceneState = rememberSceneState(entries, sceneStrategy, onBack) val scene = sceneState.currentScene ... ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  60. NavDisplay.kt @Composable public fun <T : Any> NavDisplay( sceneStrategy: SceneStrategy<T>

    = SinglePaneSceneStrategy(), ... ) { val sceneState = rememberSceneState(entries, sceneStrategy, onBack) val scene = sceneState.currentScene ... ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  61. SceneState.kt @Composable public fun <T : Any> rememberSceneState( entries: List<NavEntry<T>>,

    sceneStrategy: SceneStrategy<T>, onBack: () -> Unit, ): SceneState<T> = with(SceneStrategyScope<T>(onBack)) { ... val allScenes = mutableListOf( sceneStrategy.calculateSceneWithSinglePaneFallback(this, decoratedEntries) ) ... } ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  62. SinglePaneScene.kt internal fun <T : Any> SceneStrategy<T>.calculateSceneWithSinglePaneFallback( scope: SceneStrategyScope<T>, entries:

    List<NavEntry<T>>, ): Scene<T> { return scope.calculateScene(entries) ?: with(SinglePaneSceneStrategy<T>()) { scope.calculateScene(entries) } } ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  63. SinglePaneScene.kt public class SinglePaneSceneStrategy<T : Any> : SceneStrategy<T> { override

    fun SceneStrategyScope<T>.calculateScene( entries: List<NavEntry<T>> ): Scene<T> { return SinglePaneScene( key = entries.last().contentKey, entry = entries.last(), previousEntries = entries.dropLast(1), ) } } ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  64. SinglePaneScene.kt public class SinglePaneSceneStrategy<T : Any> : SceneStrategy<T> { override

    fun SceneStrategyScope<T>.calculateScene( entries: List<NavEntry<T>> ): Scene<T> { return SinglePaneScene( key = entries.last().contentKey, entry = entries.last(), previousEntries = entries.dropLast(1), ) } } ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  65. SinglePaneScene.kt internal data class SinglePaneScene<T : Any>( override val key:

    Any, val entry: NavEntry<T>, override val previousEntries: List<NavEntry<T>>, ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(entry) override val content: @Composable () -> Unit = { entry.Content() } ... ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  66. SinglePaneScene.kt internal data class SinglePaneScene<T : Any>( override val key:

    Any, val entry: NavEntry<T>, override val previousEntries: List<NavEntry<T>>, ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(entry) override val content: @Composable () -> Unit = { entry.Content() } ... ӝࠄ SceneStrategyח Sceneҗ NavEntryܳ 1:1۽ ׮ܛפ׮.
  67. Back stack SceneStrategy1 SceneStrategy2 Scene Strategies Ordered list NavDisplay ৈ۞

    ѐ੄ SceneStrategyܳ ࢎਊೡ ٸח ࣽࢲо ઺ਃ೤פ׮.
  68. Back stack SceneStrategy1 SceneStrategy2 Scene 1 ਸ ࢤࢿೡ ࣻ ੓ա?

    Scene1 ۪؊݂ Scene Strategies Yes No NavDisplay Scene੉ ࢤࢿؼ ٸө૑ SceneStrategy ݾ۾ਸ ࣽഥ೤פ׮.
  69. Back stack SceneStrategy1 SceneStrategy2 SinglePaneStrategy Scene 1 ਸ ࢤࢿೡ ࣻ

    ੓ա? Scene1 ۪؊݂ Scene 2 ܳ ࢤࢿೡ ࣻ ੓ա? Scene2 ۪؊݂ Scene Strategies Yes No Yes No NavDisplay Scene੉ ࢤࢿؼ ٸө૑ SceneStrategy ݾ۾ਸ ࣽഥ೤פ׮.
  70. Back stack SceneStrategy1 SceneStrategy2 SinglePaneStrategy Scene 1 ਸ ࢤࢿೡ ࣻ

    ੓ա? Scene1 ۪؊݂ Scene 2 ܳ ࢤࢿೡ ࣻ ੓ա? Scene2 ۪؊݂ SinglePaneScene ۪؊݂ Scene Strategies Yes No Yes No NavDisplay Scene੉ ࢤࢿؼ ٸө૑ SceneStrategy ݾ۾ਸ ࣽഥ೤פ׮.
  71. val sceneStrategy1 = remember { SceneStrategy1<NavKey>() } val sceneStrategy2 =

    remember { SceneStrategy2<NavKey>() } val fallbackStrategy = remember { SinglePaneSceneStrategy<NavKey>() } NavDisplay( sceneStrategy = sceneStrategy1 then sceneStrategy2 then fallbackStrategy, ... thenਵ۽ ৈ۞ о૑ SceneStrategyܳ োѾ೤פ׮.
  72. SinglePaneScene.kt internal fun <T : Any> SceneStrategy<T>.calculateSceneWithSinglePaneFallback( scope: SceneStrategyScope<T>, entries:

    List<NavEntry<T>>, ): Scene<T> { return scope.calculateScene(entries) ?: with(SinglePaneSceneStrategy<T>()) { scope.calculateScene(entries) } } SinglePaneSceneStrategy੉ fallbackਵ۽ ઁҕؾפ׮.
  73. val sceneStrategy1 = remember { SceneStrategy1<NavKey>() } val sceneStrategy2 =

    remember { SceneStrategy2<NavKey>() } val fallbackStrategy = remember { SinglePaneSceneStrategy<NavKey>() } NavDisplay( sceneStrategy = sceneStrategy1 then sceneStrategy2 then fallbackStrategy, ... SinglePaneSceneStrategy੉ fallbackਵ۽ ઁҕؾפ׮.
  74. SceneStrategy • androidx.navigation3:navigation3-ui • SinglePaneSceneStrategy • DialogSceneStrategy — OverlayScene •

    androidx.compose.material3.adaptive:adaptive-navigation3 • ListDetailSceneStrategy • SupportingPaneSceneStrategy • android/nav3-recipes (example) • BottomSheetSceneStrategy — OverlayScene • TwoPaneSceneStrategy A B A B A B ׮নೠ SceneStrategyܳ ઁҕೞҊ ੓णפ׮.
  75. Navigation 3 • State ӝ߈ • Back stack (૒੽ ࣗਬ)

    • Sceneਸ ాೠ ழझథ ۨ੉ইਓ A B A B A B A B A Single Dialog BottomSheet ListDetail TwoPane ੉ઁ Sceneਸ ੉ਊೞৈ, ׮নೠ ழझథ ۨ੉ইਓਸ ҳഅೡ ࣻ ੓णפ׮.
  76. ੸਽ഋ জ • ׮নೠ ചݶ ௼ӝী ߈਽ೞৈ, ୭੸ചػ ࢎਊ੗ ҃೷ਸ

    ઁҕೞח জ Link: h tt ps://developer.android.com/develop/ui/compose/build-adaptive-apps ੸਽ഋ ۨ੉ইਓ, ԙ ೧ঠ ೡөਃ?
  77. Android ಬ ಂఠ • Foldable, Tablet, Desktop, XR ١ ؀ഋ

    ചݶ ӝӝо טযաח ୶ࣁ (5র ؀ ੉࢚) Link: h tt ps://developer.android.com/develop#devices Google Play Console ૑಴ܳ ࠁݶ, ಫ؊࠶ ࢎਊ੗о ੼੼ טযաҊ ੓णפ׮.
  78. Android 16 ز੘ ߸҃ࢎ೦: ੸਽ഋ ۨ੉ইਓ ߑೱ, ௼ӝ ઑ੺ оמ

    ৈࠗ, о۽ࣁ۽ ࠺ਯ ઁೠ ޖद (୭ࣗ ց࠺о 600dp ੉࢚) Link: h tt ps://developer.android.com/about/versions/16/behavior-changes-16#adaptive-layouts Android 16 ࠗఠח ಫ؊࠶ ӝӝܳ ಟଢ଼ਸ ٸ, ۨ੉ইਓ ઁডࢎ೦੉ ޖदؾפ׮.
  79. Android 16 ز੘ ߸҃ࢎ೦: ੸਽ഋ ۨ੉ইਓ ׮਺ ݒפಕझ౟ ࣘࢿҗ ۠ఋ੐

    APIח ؀ഋ ചݶ ӝӝীࢲ ޖद • android:resizeableActivity="false" • android:screenOrientation="portrait" • android:minAspectRatio="2" • android:maxAspectRatio="3" • Activity#setRequestedOrientation(int) • Activity#getRequestedOrientation() Link: h tt ps://developer.android.com/about/versions/16/behavior-changes-16#adaptive-layouts Android 17ө૑ ਬ৘ೡ ࣻ ੓૑݅, ѾҴ झ݃౟ಪ ࠺ਯਸ ߩযդ ചݶ ௼ӝܳ Ҋ۰೧ঠ ೤פ׮.
  80. ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓ • ࠺ਊ ޙઁ 20:9 16:9 or 10.8 :

    9 ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓਸ ੸ਊೞݶ, ѐߊ/٣੗ੋ ࠺ਊਸ ઴ੌ ࣻ ੓णפ׮.
  81. ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓ • ࠺ਊ ޙઁ or ݽٚ ചݶਸ ؀ഋ ചݶী

    ୭੸ചػ ۨ੉ইਓਵ۽ ߸҃ೞח Ѫ਷ औ૑ ঋणפ׮.
  82. ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓ • ࠺ਊ ޙઁ 8:9 10:9 10.8 : 9

    ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓ਷ ӝઓ ചݶ 2ѐܳ աۆ൤ فח Ѫҗ ࠺त೤פ׮.
  83. ౟ۄ੉ಫ٘ • 3 Pane (list, detail, extra) List Detail Extra

    List Detail Extra о۽۽ ؊ և਷ ಬಂఠо աয়؊ۄب, Pane ࣻܳ טܻݶ ؾפ׮.
  84. START Window ց࠺о ୽࠙൤ ௾о? B No Yes A B

    Navigation 2 Content B Content A Content B Content A ୐ ߣ૩۽ ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓਸ Navigation 2۽ ҳഅ೧ࠁݶ
  85. START Window ց࠺о ୽࠙൤ ௾о? A No Yes A Content

    A Content A Navigation 2 ݾ۾ ചݶীࢲח झ݃౟ಪҗ క࠶݁੉ زੌ೤פ׮.
  86. START Window ց࠺о ୽࠙൤ ௾о? B No Yes A B

    Content B Content A Content B Content A ࣁࠗ੿ࠁ ചݶࠗఠח झ݃౟ಪҗ క࠶݁ীࢲ Back stackਸ ׮ܰѱ ҙܻ೧ঠ ೤פ׮. Navigation 2
  87. // Phone val navController = rememberNavController() NavHost(navController = navController, startDestination

    = ContentA) { composable<ContentA> { ContentAScreen(onItemClick = { navController.navigate(route = ContentB(id = 1234)) }) } composable<ContentB> { backStackEntry -> val route: ContentB = backStackEntry.toRoute() ContentBScreen(route.id) } } झ݃౟ಪীࢲח A, Bܳ ׮ܲ Destinationਵ۽ ܻ࠙೤פ׮.
  88. // Tablet val navController = rememberNavController() NavHost(navController = navController, startDestination

    = ContentAB) { composable<ContentAB> { var contentB: ContentB? by remember { mutableStateOf(null) } ContentAScreen(onItemClick = { contentB = ContentB(id = 1234) }) contentB?.let { route -> BackHandler { contentB = null } ContentBScreen(route.id) } } } క࠶݁ীࢲח A, Bܳ ೞա੄ Destinationਵ۽ ೤୛ঠ ೤פ׮.
  89. // Tablet val navController = rememberNavController() NavHost(navController = navController, startDestination

    = ContentAB) { composable<ContentAB> { var contentB: ContentB? by remember { mutableStateOf(null) } // Saveable? ContentAScreen(onItemClick = { contentB = ContentB(id = 1234) }) contentB?.let { route -> BackHandler { contentB = null } // Predictive Back? ContentBScreen(route.id) // ViewModel? } } } A, Bܳ ೞա۽ ೤஖ݶ ૒੽ ҙܻ೧ঠ ೞח ࠗ࠙੉ ݆ইઉࢲ
  90. // Phone, Tablet, Foldable composable<ContentAB> { val navigator = rememberListDetailPaneScaffoldNavigator<ScreenKey>()

    NavigableListDetailPaneScaffold( navigator = navigator, listPane = { AnimatedPane { ContentAScreen(onItemClick = { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, ContentB(1234)) }) }}, detailPane = { AnimatedPane { val route = navigator.currentDestination?.contentKey ContentBScreen(route?.id) }} ) } Compose Material3 Adaptive Navigationਸ ࢎਊೞח Ѫ੉ જणפ׮.
  91. // Phone, Tablet, Foldable composable<ContentAB> { val navigator = rememberListDetailPaneScaffoldNavigator<ScreenKey>()

    NavigableListDetailPaneScaffold( navigator = navigator, listPane = { AnimatedPane { ContentAScreen(onItemClick = { // ViewModel? navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, ContentB(1234)) }) }}, detailPane = { AnimatedPane { val route = navigator.currentDestination?.contentKey ContentBScreen(route?.id) // ViewModel? }} ) } ׮݅ ৈӝࢲب Pane݃׮ ViewModelਸ աׂࣻ হח ١ ইए਍ ੼੉ ੓णפ׮.
  92. Activity Embedding • Bottom Navigation + ژ ׮ܲ ৘۽ Activity

    Embeddingীࢲח, Bottom Navigation੉ ௾ ചݶীࢲ য࢝ೞѱ ಴दغחؘਃ.
  93. Activity Embedding • Bottom Navigation — Navigation Rail + ௾

    ചݶীࢲח Navigation Rail۽ ಴दೞח Ѫ੉ ؊ ੗োझ۞ਕ ࠁੑפ׮.
  94. START Window ց࠺о ୽࠙൤ ௾о? SinglePaneScene B No Navigation 3

    + Scene Navigation 3ীࢲח Sceneਵ۽ ੉۠ ࠗ࠙ਸ औѱ ҳഅೡ ࣻ ੓णפ׮.
  95. START Window ց࠺о ୽࠙൤ ௾о? SinglePaneScene B No Entry ٜ੉

    ೙ਃೠ metadataܳ ыҊ ੓ա? Yes Yes No ListDetailScene A B NavEntry B NavEntry A Navigation 3 + Scene Navigation 3ীࢲח Sceneਵ۽ ੉۠ ࠗ࠙ਸ औѱ ҳഅೡ ࣻ ੓णפ׮.
  96. START Window ց࠺о ୽࠙൤ ௾о? SinglePaneScene B No Entry ٜ੉

    ೙ਃೠ metadataܳ ыҊ ੓ա? Yes Yes No ListDetailScene A B NavEntry B NavEntry A Navigation 3 + Scene NavEntry C BottomSheetScene A C Navigation 3ীࢲח Sceneਵ۽ ੉۠ ࠗ࠙ਸ औѱ ҳഅೡ ࣻ ੓णפ׮.
  97. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy(), entryProvider = entryProvider { entry<MovieList>( metadata = ListDetailSceneStrategy.listPane() ) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie>( metadata = ListDetailSceneStrategy.detailPane() ) { movie -> MovieScreen(movie.id) } }) ListDetailSceneStrategyܳ ୶оೞҊ
  98. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy(), entryProvider = entryProvider { entry<MovieList>( metadata = ListDetailSceneStrategy.listPane() ) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie>( metadata = ListDetailSceneStrategy.detailPane() ) { movie -> MovieScreen(movie.id) } }) NavEntryী ചݶ੄ Roleਸ ࢸ੿೤פ׮.
  99. ListDetailSceneStrategy.kt public companion object { internal const val ListDetailRoleKey: String

    = "androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole" public fun listPane( sceneKey: Any = Unit, detailPlaceholder: @Composable ThreePaneScaffoldScope.() -> Unit = {}, ): Map<String, Any> = mapOf( ListDetailRoleKey to ListMetadata(sceneKey, detailPlaceholder) ) ... } ࢶఖࢎ೦ਵ۽ sceneKeyب ࢸ੿ೡ ࣻ ੓णפ׮.
  100. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy(), entryProvider = entryProvider { entry<MovieList>( metadata = ListDetailSceneStrategy.listPane(sceneKey = "scene1") ) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie>( metadata = ListDetailSceneStrategy.detailPane(sceneKey = "scene2") ) { movie -> MovieScreen(movie.id) } }) MovieList Movie sceneKeyо ׮ܰݶ, ೞա੄ ‘ݾ۾-ࣁࠗ੿ࠁ’ ۨ੉ইਓਵ۽ ޘ੉૑ ঋणפ׮.
  101. ListDetailSceneStrategy.kt public class ListDetailSceneStrategy<T : Any>(... ) : SceneStrategy<T> {

    override fun SceneStrategyScope<T>.calculateScene( entries: List<NavEntry<T>> ): Scene<T>? { val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null ... } ղࠗܳ ࢓ಝࠁݶ, ’ݾ۾-ࣁࠗ੿ࠁ’ metadataܳ ӝ߈ਵ۽ Sceneਸ ࢤࢿ೤פ׮.
  102. ListDetailSceneStrategy.kt public class ListDetailSceneStrategy<T : Any>(... ) : SceneStrategy<T> {

    override fun SceneStrategyScope<T>.calculateScene( entries: List<NavEntry<T>> ): Scene<T>? { val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null ... } private fun <T : Any> getPaneMetadata(entry: NavEntry<T>): PaneMetadata? = entry.metadata[ListDetailRoleKey] as? PaneMetadata ղࠗܳ ࢓ಝࠁݶ, ’ݾ۾-ࣁࠗ੿ࠁ’ metadataܳ ӝ߈ਵ۽ Sceneਸ ࢤࢿ೤פ׮.
  103. ListDetailSceneStrategy.kt val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null val sceneKey

    = lastPaneMetadata.sceneKey val scaffoldEntries = mutableListOf<NavEntry<T>>() var idx = entries.lastIndex while (idx >= 0) { val entry = entries[idx] val paneMetadata = getPaneMetadata(entry) ?: break if (paneMetadata.sceneKey == sceneKey) { scaffoldEntries.add(0, entry) } idx-- } ୭࢚ױ NavEntry੄ sceneKey৬ زੌೠ NavEntryܳ ଺ই
  104. ListDetailSceneStrategy.kt val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null val sceneKey

    = lastPaneMetadata.sceneKey val scaffoldEntries = mutableListOf<NavEntry<T>>() var idx = entries.lastIndex while (idx >= 0) { val entry = entries[idx] val paneMetadata = getPaneMetadata(entry) ?: break if (paneMetadata.sceneKey == sceneKey) { scaffoldEntries.add(0, entry) } idx-- } ୭࢚ױ NavEntry੄ sceneKey৬ زੌೠ NavEntryܳ ଺ই
  105. ListDetailSceneStrategy.kt val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null val sceneKey

    = lastPaneMetadata.sceneKey val scaffoldEntries = mutableListOf<NavEntry<T>>() var idx = entries.lastIndex while (idx >= 0) { val entry = entries[idx] val paneMetadata = getPaneMetadata(entry) ?: break if (paneMetadata.sceneKey == sceneKey) { scaffoldEntries.add(0, entry) } idx-- } ୭࢚ױ NavEntry੄ sceneKey৬ زੌೠ NavEntryܳ ଺ই
  106. ListDetailSceneStrategy.kt val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null val sceneKey

    = lastPaneMetadata.sceneKey val scaffoldEntries = mutableListOf<NavEntry<T>>() var idx = entries.lastIndex while (idx >= 0) { val entry = entries[idx] val paneMetadata = getPaneMetadata(entry) ?: break if (paneMetadata.sceneKey == sceneKey) { scaffoldEntries.add(0, entry) } idx-- } ୭࢚ױ NavEntry੄ sceneKey৬ زੌೠ NavEntryܳ ଺ই
  107. ListDetailSceneStrategy.kt val lastPaneMetadata = getPaneMetadata(entries.last()) ?: return null val sceneKey

    = lastPaneMetadata.sceneKey val scaffoldEntries = mutableListOf<NavEntry<T>>() var idx = entries.lastIndex while (idx >= 0) { val entry = entries[idx] val paneMetadata = getPaneMetadata(entry) ?: break if (paneMetadata.sceneKey == sceneKey) { scaffoldEntries.add(0, entry) } idx-- } ୭࢚ױ NavEntry੄ sceneKey৬ زੌೠ NavEntryܳ ଺ই
  108. ListDetailSceneStrategy.kt if (scaffoldEntries.isEmpty()) return null val scene = ThreePaneScaffoldScene( key

    = sceneKey, onBack = onBack, ... scaffoldEntries = scaffoldEntries, ) if (scene.currentScaffoldValue.paneCount <= 1) { return null } return scene } ‘ݾ۾-ࣁࠗ੿ࠁ’ Sceneਸ ҳࢿ೤פ׮.
  109. ListDetailSceneStrategy.kt if (scaffoldEntries.isEmpty()) return null val scene = ThreePaneScaffoldScene( key

    = sceneKey, onBack = onBack, ... scaffoldEntries = scaffoldEntries, ) if (scene.currentScaffoldValue.paneCount <= 1) { return null } return scene } Pane੉ 2ѐ ੉࢚ੌ ٸ݅ ‘ݾ۾-ࣁࠗ੿ࠁ’ Sceneਵ۽ ಴द೤פ׮.
  110. ListDetailSceneStrategy.kt if (scaffoldEntries.isEmpty()) return null val scene = ThreePaneScaffoldScene( key

    = sceneKey, onBack = onBack, ... scaffoldEntries = scaffoldEntries, ) if (scene.currentScaffoldValue.paneCount <= 1) { return null } return scene } Pane੉ 2ѐ ੉࢚ੌ ٸ݅ ‘ݾ۾-ࣁࠗ੿ࠁ’ Sceneਵ۽ ಴द೤פ׮.
  111. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy(), entryProvider = entryProvider { entry<MovieList>( metadata = ListDetailSceneStrategy.listPane(sceneKey = "scene1") ) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie>( metadata = ListDetailSceneStrategy.detailPane(sceneKey = "scene1") ) { movie -> MovieScreen(movie.id) } }) MovieList Movie sceneKeyܳ ࢎਊೡ ٸח ч੉ زੌೞب۾ ࢸ੿ী ઱੄ೞࣁਃ.
  112. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy(), entryProvider = entryProvider { entry<MovieList>( metadata = ListDetailSceneStrategy.listPane() ) { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie>( metadata = ListDetailSceneStrategy.detailPane() ) { movie -> MovieScreen(movie.id) } }) ListDetailSceneStrategy APIܳ ࠁݶ,
  113. ListDetailSceneStrategy.kt @Composable public fun <T : Any> rememberListDetailSceneStrategy( backNavigationBehavior: BackNavigationBehavior

    = BackNavigationBehavior.PopUntilScaffoldValueChange, directive: PaneScaffoldDirective = calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()), adaptStrategies: ThreePaneScaffoldAdaptStrategies = ListDetailPaneScaffoldDefaults.adaptStrategies(), ): ListDetailSceneStrategy<T> { ... BackNavigationBehaviorо ੓חؘਃ.
  114. ListDetailSceneStrategy.kt internal class ThreePaneScaffoldScene<T : Any>(...) : Scene<T> { ...

    override val content: @Composable () -> Unit = { ... NavigationBackHandler( // local handler state = gestureState, isBackEnabled = previousScaffoldValue != null, onBackCompleted = { repeat(allEntries.size - onBackResult.previousEntries.size) { onBack() } }, ) ... } } ListDetailScene ղࠗীࢲ ׮ܖח Back ੉߮౟ ੿଼ਸ ࢶఖೞחؘ ࢎਊ೤פ׮.
  115. ListDetailSceneStrategy.kt @Composable public fun <T : Any> rememberListDetailSceneStrategy( backNavigationBehavior: BackNavigationBehavior

    = BackNavigationBehavior.PopUntilScaffoldValueChange, directive: PaneScaffoldDirective = calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()), adaptStrategies: ThreePaneScaffoldAdaptStrategies = ListDetailPaneScaffoldDefaults.adaptStrategies(), ): ListDetailSceneStrategy<T> { ... ӝࠄчਸ ױࣽೠ Back ز੘ਵ۽ ߸҃ೞݶ
  116. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy( backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange ), entryProvider = entryProvider { ... } ) ӝࠄчਸ ױࣽೠ Back ز੘ਵ۽ ߸҃ೞݶ
  117. val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, sceneStrategy =

    rememberListDetailSceneStrategy( backNavigationBehavior = BackNavigationBehavior.PopLatest ), entryProvider = entryProvider { ... } ) ӝࠄчਸ ױࣽೠ Back ز੘ਵ۽ ߸҃ೞݶ
  118. // navigation3 1.1.0 + SharedTransitionLayout { NavDisplay( sceneStrategy = rememberTwoPaneSceneStrategy<NavKey>(),

    + sharedTransitionScope = this, entryProvider = entryProvider { entry<Home>( metadata = TwoPaneScene.twoPane() ) { ... } entry<Product>( metadata = TwoPaneScene.twoPane() ) { ... } } ) + } Nav3 1.1 ߡ੹ীࢲח sharedTransitionScope чਸ ੹׳ೞݶ
  119. • Android 16 ز੘ ߸҃ࢎ೦ਵ۽, ੸਽ഋ ۨ੉ইਓ ؀਽੉ ೙ࣻ੸ਵ۽ ਃҳػ׮.

    • ‘Navigation 3’৬ ‘Compose Material3 Adaptive’ ۄ੉࠳۞ܻܳ Ѿ೤ೞৈ ױੌ Back stackਵ۽ ੸਽ഋ UIܳ ҳഅೡ ࣻ ੓׮. • ‘ݾ۾-ࣁࠗ੿ࠁ(List-detail)’ ۨ੉ইਓ਷ ׮নೠ ಬಂఠܳ ؀਽ೞחؘ ٜযоח ѐߊ/٣੗ੋ ࠺ਊਸ ઴ੌ ࣻ ੓׮. • ⚠ ױ, Single Activity + Compose ҳઑ۽ ੹ജೞח ࠺ਊ੉ ੓׮. Recap
  120. Migration from • Multiple activities — “ए਍ ߑߨ੉ হ׮…” •

    Nav2 (fragment) ژח Multiple fragments • Nav2 (compose) // androidx.fragment:fragment-compose:1.8 AndroidFragment<MyFragment>( arguments = Bundle().apply { putString("key" to "value") } ) AndroidFragment ١ਸ ੉ਊೞৈ, п ചݶਸ Compose ӝ߈ਵ۽ ੹ജ೧ঠ ೤פ׮.
  121. Setup dependencies { // Navigation3 implementation "androidx.navigation3:navigation3-runtime:1.0.0" implementation "androidx.navigation3:navigation3-ui:1.0.0" implementation

    "androidx.activity:activity:1.12.0" implementation "androidx.appcompat:appcompat:1.7.1" // + ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel-navigation3:2.10.0" implementation "androidx.hilt:hilt-lifecycle-viewmodel-compose:1.3.0" // Hilt ࢎਊ द // + Adaptive Layout implementation "androidx.compose.material3.adaptive:adaptive-navigation3:1.3.0-alpha04" }
  122. Navigation 2 @Serializable data object MovieList @Serializable data class Movie(val

    id: Int) Navigation 3 @Serializable data object MovieList : NavKey @Serializable data class Movie(val id: Int) : NavKey
  123. Navigation 2 val navController = rememberNavController() NavHost( navController = navController,

    startDestination = MovieList, ) { ... Navigation 3 val backStack = rememberNavBackStack(MovieList) NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, ) { ...
  124. Navigation 2 NavHost(...) { composable<MovieList> { MovieListScreen(onItemClick = { navController.navigate(Movie(it.id))

    }) } composable<Movie> { entry -> val movie = entry.toRoute<Movie>() MovieScreen(movie.id) } ) Navigation 3 NavDisplay( entryProvider = entryProvider { entry<MovieList> { MovieListScreen(onItemClick = { backStack += Movie(it) }) } entry<Movie> { movie -> MovieScreen(movie.id) } } )
  125. Navigation 2 NavHost(...) { navigation<Game>(startDestination = Match) { composable<Match> {

    MatchScreen(...) } ... } ) val navController = rememberNavController() navController.handleDeeplink(intent) Navigation 3 NavDisplay( // X ) // X
  126. Navigation 2 NavHost( ... enterTransition = { a() }, exitTransition

    = { b() }, popEnterTransition = { c() }, popExitTransition = { d() }, ) { ... } Navigation 3 NavDisplay( ... transitionSpec = { a() togetherWith b() }, popTransitionSpec = { c() togetherWith d() }, predictivePopTransitionSpec = { ... }, )
  127. Navigation 2 SharedTransitionLayout { NavHost(...) { composable<Movie> { with(this@SharedTransitionLayout) {

    MovieScreen( modifier = Modifier.sharedElement( rememberSharedContentState("key"), animatedVisibilityScope = this@composable, ), ) } } ... } } Navigation 3 SharedTransitionLayout { NavDisplay(entryProvider = entryProvider { entry<Movie> { with(this@SharedTransitionLayout) { MovieScreen( modifier = Modifier.sharedElement( rememberSharedContentState("key"), animatedVisibilityScope = LocalNavAnimatedContentScope.current, ), ) } } ... }) }
  128. Ѿҗח? • Compile: ✅ Success! • Runtime: ❌ Failure… ׮݅

    Compile੉ ࢿҕೞ؊ۄب, प೯೧ࠁݶ Runtime য়ܨо ߊࢤೞѢա ӝઓ ௏٘о ־ۅؼ ࣻ ੓णפ׮.
  129. Issue: App Crash • জਸ प೯ೞݶ, ߄۽ ௼ېदо ߊࢤೡ ࣻ

    ੓׮. • Jetpack ۄ੉࠳۞ܻ ߡ੹ਸ সؘ੉౟ ೧ঠೠ׮. • ઁझ୊ ղ࠺ѱ੉࣌ ૑ਗ • androidx.activity সؘ੉౟ ೙ਃ Link: h tt ps://github.com/android/nav3-recipes/issues/6 java.lang.IllegalStateException: No NavigationEventDispatcher was provided via LocalNavigationEventDi at androidx.navigationevent.compose.NavigationEventHandlerKt.NavigationEventHandler(NavigationEve at androidx.navigationevent.compose.NavigationEventHandlerKt.NavigationBackHandler(NavigationEven at androidx.navigation3.ui.NavDisplayKt__NavDisplayKt.NavDisplay(NavDisplay.kt:296) at androidx.navigation3.ui.NavDisplayKt.NavDisplay(Unknown Source:1) dependencies { + implementation "androidx.activity:activity:1.12.0" + implementation "androidx.appcompat:appcompat:1.7.1" }
  130. Issue: App Crash • ਗੋ: androidx.navigationevent (Back ੉߮౟ ҙ۲ KMP-first

    API ઁҕ) • (1) NavigationEventDispatcherOwnerܳ ઁҕೞ૑ ঋח ComponentActivity • (2) ࢚ࣘ੉ ੜޅػ AppCompatActivity Link: h tt ps://android-review.googlesource.com/c/pla tf orm/frameworks/suppo rt /+/3619411 open class ComponentActivity() : androidx.core.app.ComponentActivity(), + NavigationEventDispatcherOwner, ... { open fun initializeViewTreeOwners() { ... + window.decorView.setViewTreeNavigationEventDispatcherOwner(this) } (1) Navigation Eventܳ ૑ਗೞח ୭न Activity ߡ੹ਸ ࢎਊ೧ঠ ೤פ׮.
  131. AppCompatActivity.java public void setContentView(@LayoutRes int layoutResID) { - initViewTreeOwners(); +

    initializeViewTreeOwners(); getDelegate().setContentView(layoutResID); } - private void initViewTreeOwners() { - ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this); - ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this); - ViewTreeSavedStateRegistryOwner.set(getWindow().getDecorView(), this); - ViewTreeOnBackPressedDispatcherOwner.set(getWindow().getDecorView(), this); - } Link: h tt ps://android-review.googlesource.com/c/pla tf orm/frameworks/suppo rt /+/3642791 (2) AppCompatActivityীࢲ ੜޅ ୊ܻೞ؍ ࠗ࠙੉ ࣻ੿غ঻णפ׮.
  132. Issue: Double-click • Custom Back ߡౡਸ োఋೞݶ, ௼ېदо ߊࢤೡ ࣻ

    ੓׮. • Back stack਷ ୭ࣗ 1ѐ੄ ೦ݾ੉ ੓যঠ ೠ׮. Link: h tt ps://github.com/android/nav3-recipes/issues/6 java.lang.IllegalArgumentException: NavDisplay backstack cannot be empty Button(onClick = { backStack.removeLastOrNull() })
  133. Button(onClick = { if (backStack.size > 1) { backStack.removeLastOrNull() }

    }) Back stack੄ ѐࣻܳ ഛੋೞח ઑѤޙਸ ୶оೞѢա.
  134. // lifecycle-compose 2.8.0 Button(onClick = dropUnlessResumed { backStack.removeLastOrNull() }) dropUnlessResumed

    API۽ хऱח ߑߨ੉ ੓णפ׮. Link: h tt ps://developer.android.com/jetpack/androidx/releases/lifecycle#2.8.0
  135. DropUnlessLifecycle.kt @Composable public fun dropUnlessResumed( lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current, block:

    () -> Unit, ): () -> Unit { ... return { if (lifecycleOwner.lifecycle.currentState.isAtLeast(State.RESUMED)) { block() } } } Link: h tt ps://developer.android.com/jetpack/androidx/releases/lifecycle#2.8.0 ׮ܲ ചݶਵ۽੄ ੹ജ੉ ੉޷ द੘ػ റ, ੉߮౟ܳ ୊ܻೞ૑ ঋب۾ ઁೠೞח API ੑפ׮.
  136. Issue: ViewModel • NavEntry ղࠗীࢲ ViewModelਸ ࢤࢿೞݶ Activity ߧਤ۽ ࢤࢿػ׮.

    Link: h tt ps://developer.android.com/guide/navigation/navigation-3/save-state#scoping-viewmodels NavDisplay(entryProvider = entryProvider { entry<Movie> { key -> MovieScreen(viewModel = viewModel(factory = MovieViewModel.Factory(key))) } }) dependencies { + implementation "androidx.lifecycle:lifecycle-viewmodel-navigation3:2.10.0" } Activity ߧਤ ؀न NavEntry ߧਤ۽ ViewModelਸ ࢤࢿೞ۰ݶ, ۄ੉࠳۞ܻܳ ୶оೞҊ
  137. NavDisplay( entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), + rememberViewModelStoreNavEntryDecorator(), ), entryProvider =

    entryProvider { ... entry<Movie> { key -> MovieScreen(viewModel = viewModel( factory = MovieViewModel.Factory(key) )) } } ) Link: h tt ps://developer.android.com/guide/navigation/navigation-3/save-state#scoping-viewmodels ViewModelStoreNavEntryDecoratorܳ ୶оೞݶ ؾפ׮.
  138. ViewModelStoreNavEntryDecorator.kt public class ViewModelStoreNavEntryDecorator<T : Any>(...) : NavEntryDecorator<T>( onPop =

    { ... } decorate = { entry -> ... val childViewModelStoreOwner = remember { ... } CompositionLocalProvider( LocalViewModelStoreOwner provides childViewModelStoreOwner ) { entry.Content() } } ) ղࠗ੸ਵ۽ NavEntry݃׮ ViewModelStoreOwnerܳ ҙܻ೧સפ׮. Link: h tt ps://developer.android.com/guide/navigation/navigation-3/save-state#scoping-viewmodels
  139. Issue: ViewModel + SavedStateHandle • Nav2 ୊ۢ SavedStateHandleী argumentsо ઁҕغ૑

    ঋח׮. • ViewModel Factoryܳ ా೧, ૒੽ чਸ ੹׳೧ঠ ೠ׮. Link: h tt ps://github.com/android/nav3-recipes/issues/86 // Navigation2 class MovieViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private val movie: Movie = savedStateHandle.toRoute<Movie>() }
  140. NavDisplay(entryProvider = entryProvider { entry<Movie> { movie -> MovieScreen(viewModel =

    viewModel(factory = MovieViewModel.Factory(movie))) } }) ViewModelਸ ࢤࢿೡ ٸ, keyܳ ੹׳೤פ׮.
  141. NavDisplay(entryProvider = entryProvider { entry<Movie> { movie -> MovieScreen(viewModel =

    viewModel(factory = MovieViewModel.Factory(movie))) } }) class MovieViewModel(private val movie: Movie) : ViewModel() { ... class Factory(private val movie: Movie) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return MovieViewModel(movie) as T } } } ViewModelਸ ࢤࢿೡ ٸ, keyܳ ੹׳೤פ׮.
  142. NavDisplay(entryProvider = entryProvider { entry<Movie> { movie -> MovieScreen(viewModel =

    viewModel(factory = MovieViewModel.Factory(movie))) } }) class MovieViewModel(private val movie: Movie) : ViewModel() { ... class Factory(private val movie: Movie) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return MovieViewModel(movie) as T } } } ViewModelਸ ࢤࢿೡ ٸ, keyܳ ੹׳೤פ׮.
  143. NavDisplay(entryProvider = entryProvider { entry<Movie> { movie -> MovieScreen(viewModel =

    viewModel(factory = MovieViewModel.Factory(movie))) } }) class MovieViewModel(private val movie: Movie) : ViewModel() { ... class Factory(private val movie: Movie) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return MovieViewModel(movie) as T } } } ViewModelਸ ࢤࢿೡ ٸ, keyܳ ੹׳೤פ׮.
  144. Issue: HiltViewModel + SavedStateHandle • @HiltViewModelীࢲ SavedStateHandleਸ ࢎਊೞҊ ੓঻׮ݶ, Factory

    ҙ۲ ௏٘ܳ ҳഅ೧ঠ ೠ׮. - val viewModel = hiltViewModel<MovieViewModel>() + val viewModel = hiltViewModel<MovieViewModel, MovieViewModel.Factory>( + creationCallback = { factory -> factory.create(key) } + ) dependencies { + implementation "androidx.hilt:hilt-lifecycle-viewmodel-compose:1.3.0" }
  145. // Navigation 2 val viewModel = hiltViewModel<MovieViewModel>() @HiltViewModel class MovieViewModel

    @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { private val movie: Movie = savedStateHandle.toRoute<Movie>() } SavedStateHandleҗ toRouteܳ ࢎਊೞח Ѫ ؀नী,
  146. // Navigation 3 val viewModel = hiltViewModel<MovieViewModel, MovieViewModel.Factory>( creationCallback =

    { factory -> factory.create(key) } ) @HiltViewModel(assistedFactory = MovieViewModel.Factory::class) class MovieViewModel @AssistedInject constructor( @Assisted val movie: Movie ) : ViewModel() { @AssistedFactory interface Factory { fun create(movie: Movie): MovieViewModel } } ҕध ࢠ೒ীࢲח Factory + Assisted Injectionਸ о੉٘ೞҊ ੓णפ׮.
  147. ViewModelStoreNavEntryDecorator.kt (modified) public class ViewModelStoreNavEntryDecorator<T : Any>(...) : NavEntryDecorator<T>( decorate

    = { entry -> val childViewModelStoreOwner = remember { ... override val defaultViewModelCreationExtras: CreationExtras get() = MutableCreationExtras().also { it[SAVED_STATE_REGISTRY_OWNER_KEY] = this it[VIEW_MODEL_STORE_OWNER_KEY] = this + entry.key?.let { key -> it[DEFAULT_ARGS_KEY] = ... } } } } ) ӝઓ ௏٘ܳ ਬ૑ೞҊ र׮ݶ, NavEntryDecoratorܳ ૒੽ ٜ݅য argumentsܳ ੹׳ೡ ࣻب ੓णפ׮. (ӂ੢ೞ૑ ঋ਺) Link: h tt ps://github.com/fornewid/android-samples/pull/9
  148. Issue: Shared ViewModel • Nav3ীࢲח ‘઺୏ Ӓې೐’ܳ ૑ਗೞ૑ ঋח׮. •

    ৻ࠗীࢲ ղࠗ ചݶী ੽Ӕೡ ࣻ হب۾ ஭ङച — ݽٕਸ ܻ࠙ೞח ߑߨ • ৈ۞ NavEntryীࢲ ೞա੄ ViewModel ҕਬ — NavEntryDecorator ୶о? Link: h tt ps://github.com/android/nav3-recipes/issues/112 One ViewModel Two ViewModel Main Shared ViewModel One Two
  149. // Navigation 2 NavHost(navController, startDestination = "Main") { navigation(startDestination =

    "One", route = "Main") { composable("One") { val viewModel = hiltViewModel(navController.getBackStackEntry("Main")) } composable("Two") { val viewModel = hiltViewModel(navController.getBackStackEntry("Main")) } } } Nav2ח NavBackStackEntryо ViewModelStoreOwnerੋ ੼ਸ ੉ਊೞৈ, Shared ViewModelਸ ઁҕ೮णפ׮.
  150. // Navigation 3 (NOT official) NavDisplay(entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberSharedViewModelStoreNavEntryDecorator(),

    ), entryProvider = entryProvider { entry<One> { val viewModel = hiltViewModel<MainViewModel>() } entry<Two>( metadata = SharedViewModelStoreNavEntryDecorator.viewModelParent("One") ) { val viewModel = hiltViewModel<MainViewModel>() } } ) Nav3ח NavEntryDecorator۽ Shared ViewModelܳ ҳഅೞח ߑߨী ؀ೠ ൦౟ܳ ઁҕೞ૑݅, ৡ੹ೞ૓ ঋणפ׮. Link: SharedViewModelStoreNavEntryDecorator.kt One ViewModel Two
  151. Android vs KMP Language UI Image Loading DI Java, Kotlin

    Kotlin View, Jetpack Compose Compose Multiplatform Glide, Coil Coil Network Database Retrofit Ktor Dagger, Hilt Koin, Kodein, kotlin-inject Room Android Kotlin Multiplatform ... ... ... Room Navigation 2, 3 Navigation Activity, Navigation 2, 3 ݃૑݄ਵ۽ Compose Multiplatformীࢲب Nav3ܳ ࢎਊೡ ࣻ ੓णפ׮.