<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>navigation</title>
	<atom:link href="https://bps-e.com/dev/tag/navigation/feed/" rel="self" type="application/rss+xml" />
	<link>https://bps-e.com/dev</link>
	<description>android アプリ開発 kotlin + jetpack compose + material 3</description>
	<lastBuildDate>Tue, 02 Jan 2024 17:20:46 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>

<image>
	<url>https://bps-e.com/dev/wp-content/uploads/2022/10/cropped-logo3-32x32.png</url>
	<title>navigation</title>
	<link>https://bps-e.com/dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>[Android] 009. androidx.navigation</title>
		<link>https://bps-e.com/dev/android-009/</link>
		
		<dc:creator><![CDATA[bps-e]]></dc:creator>
		<pubDate>Fri, 28 Oct 2022 02:00:09 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Jetpack Compose]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Material 3]]></category>
		<category><![CDATA[navigation]]></category>
		<guid isPermaLink="false">https://bps-e.com/dev/?p=180</guid>

					<description><![CDATA[Hedgehogで確認したこのページの内容から加筆したものは以下ですhttps://bps-e.com/dev/android-004-003/ １画面のアプリであっても拡張性のためベースにNavigationを適応する [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p><strong><span class="marker-under-red">Hedgehogで確認したこのページの内容から加筆したものは以下です</span></strong><br><a href="https://bps-e.com/dev/android-004-003/">https://bps-e.com/dev/android-004-003/</a></p>



<p></p>



<p>１画面のアプリであっても拡張性のためベースにNavigationを適応するようにします</p>



<p>参考<br><a rel="noopener" href="https://developer.android.com/jetpack/compose/navigation?hl=ja" target="_blank">https://developer.android.com/jetpack/compose/navigation?hl=ja</a></p>



<h2 class="wp-block-heading"><span id="toc1">依存関係設定</span></h2>



<h3 class="wp-block-heading"><span id="toc2">Project Structuer 設定</span></h3>



<p>以下の最新を追加<br>androidx.navigation:navigation-compose<br>androidx.navigation:navigation-common-ktx <br>navigation-commonはNavGraphBuilderを使う場合に必要</p>



<h2 class="wp-block-heading"><span id="toc3">実装</span></h2>



<h3 class="wp-block-heading"><span id="toc4">シンプルな実装例</span></h3>



<p>NavHostのstartDestinationに初期画面を指定、composable()に画面UIを実装<br>navigate()で遷移、navigateUp()で戻る<br>popBackStack()で戻る場合は端末の戻るボタンと同じ動作になります</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>@Composable fun AppContent() {
    MaterialTheme {
        var navController = rememberNavController()
        NavHost(navController = navController, startDestination = &quot;top&quot;) {
            composable(&quot;top&quot;) {
                // 画面遷移
                Button(onClick = { navController.navigate(&quot;next&quot;) }) {
                    Text(&quot;top&quot;)
                }
            }
            composable(&quot;next&quot;) {
                // 前の画面に戻る
                Button(onClick = { navController.navigateUp() }) {
                    Text(&quot;next&quot;)
                }
            }
        }
    }
}</code></pre></div>



<h3 class="wp-block-heading"><span id="toc5">２画面以上で２画面以上戻る場合</span></h3>



<p>popBackStackで遷移先を指定しinclusive/saveStateにfalseを指定します</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>@Composable fun AppContent() {
    MaterialTheme {
        var navController = rememberNavController()
        NavHost(navController = navController, startDestination = &quot;top&quot;) {
            composable(&quot;top&quot;) {
                Button(onClick = { navController.navigate(&quot;next&quot;) }) {
                    Text(&quot;top&quot;)
                }
            }
            composable(&quot;next&quot;) {
                Button(onClick = { navController.navigate(&quot;last&quot;) }) {
                    Text(&quot;next&quot;)
                }
            }
            composable(&quot;last&quot;) {
                // topに戻る
                Button(onClick = { navController.popBackStack(route = &quot;top&quot;, inclusive = false, saveState = false) }) {
                    Text(&quot;last&quot;)
                }
            }
        }
    }
}</code></pre></div>



<h3 class="wp-block-heading"><span id="toc6">NavGraphBuilderを使用する場合</span></h3>



<p>NavGraphBuilderを拡張して実装します</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>fun NavGraphBuilder.topGraph() {
    composable(route = &quot;top&quot;) {
        ...
    }
}

fun NavGraphBuilder.nextGraph() {
    composable(route = &quot;next&quot;) {
        ...
    }
}

@Composable fun App() {
    ...
    NavHost(navController = navController, startDestination = &quot;top&quot;) {
        topGraph()
        nextGraph()
    }
}</code></pre></div>



<h3 class="wp-block-heading"><span id="toc7">実際の実装例</span></h3>



<p>navControllerを全体をコンポーザブルに直接渡すとテストや再利用時しにくくなるため、トリガーするナビゲーション アクションを定義するコールバックを使います</p>



<p>また以下のようにパターン化した実装にすることで画面の実装に集中できます<br>(NavRouteに項目増やしてScreenを実装してNavGrapにセットするだけ)</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>@Composable fun AppContent(appState: AppState = rememberAppState()) {
    MaterialTheme {
        AppNavHost(appState.navController, appState::onBack, appState::onNavigate)
    }
}

// AppState.kt
@Stable class AppState(val navController: NavHostController) {
    fun onNavigate(route: String) {
        navController.navigate(route)
    }

    fun onBack(route: String = &quot;&quot;) {
        if (route.isEmpty()) navController.navigateUp()
        else navController.popBackStack(route, inclusive = false, saveState = false)
    }
}
@Composable fun rememberAppState(navController: NavHostController = rememberNavController()): AppState = remember(navController) { AppState(navController) }

// AppNavHost.kt
enum class NavRoute {
    Top,
    Next;
}

fun NavGraphBuilder.navGraph(route: NavRoute, onBack: (String) -&gt; Unit, onNavigate: (String) -&gt; Unit) {
    composable(route = route.name) {
        when(route) {
            NavRoute.Top -&gt; TopScreen(onNavigate = { onNavigate(it) })
            NavRoute.Next -&gt; NextScreen(onBack = { onBack(it) })
        }
    }
}

@Composable fun AppNavHost(navController: NavHostController, onBack: (String) -&gt; Unit, onNavigate: (String) -&gt; Unit) {
    NavHost(navController = navController, startDestination = NavRoute.Top.name) {
        NavRoute.values().forEach { navGraph(it, onBack, onNavigate) }
    }
}

// TopScreen.kt
@Composable fun TopScreen(onNavigate: (String) -&gt; Unit) {
    Button(onClick = { onNavigate(NavRoute.Next.name) }) {
        Text(NavRoute.Top.name)
    }
}

// NextScreen.kt
@Composable fun NextScreen(onBack: (String) -&gt; Unit) {
    Button(onClick = { onBack(&quot;&quot;) }) {
        Text(NavRoute.Next.name)
    }
}</code></pre></div>



<h3 class="wp-block-heading"><span id="toc8">データを渡す</span></h3>



<p>navigateの引数に文字列で値を渡します<br>使いづらい&#8230;<br>この実装の場合はパラメータ無し(空文字)の場合Exceptionが発生します</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>@OptIn(ExperimentalMaterial3Api::class)
@Composable fun AppContent() {
    MaterialTheme {
        var navController = rememberNavController()
        NavHost(navController = navController, startDestination = &quot;top&quot;) {
            composable(&quot;top&quot;) {
                var text by remember { mutableStateOf(&quot;&quot;) }

                Column {
                    TextField(value = text, onValueChange = { text = it })
                    Button(onClick = {
                        navController.navigate(&quot;next/${text}/1&quot;)
                    }) {
                        Text(&quot;top&quot;)
                    }
                }
            }
            composable(&quot;next/{text}/{id}&quot;,
                arguments = listOf(
                    navArgument(&quot;text&quot;) { type = NavType.StringType },
                    navArgument(&quot;id&quot;) { type = NavType.IntType },
                )
            ) {
                Column {
                    val text = it.arguments?.getString(&quot;text&quot;) ?: &quot;&quot;
                    val id = it.arguments?.getInt(&quot;id&quot;) ?: 0
                    Text(&quot;${text}_${id}&quot;)
                    Button(onClick = { navController.navigateUp() }) {
                        Text(&quot;next&quot;)
                    }
                }
            }
        }
    }
}</code></pre></div>



<p>引数省略可能にする場合は以下のようにします</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-kt" data-lang="Kotlin"><code>composable(&quot;top&quot;) {
    ...
    Button(onClick = {
        navController.navigate(&quot;next?text=${text}&id=1&quot;)
    }) {
        ...
    }
}
composable(&quot;next?text={text}&id={id}&quot;,
    arguments = listOf(
        navArgument(&quot;text&quot;) {
            type = NavType.StringType
            defaultValue = &quot;&quot;
        },
        navArgument(&quot;id&quot;) {
            type = NavType.IntType
            defaultValue = 0
        }
    )
) {
    ...
}</code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="has-text-align-right">Android Studio Dolphin 2021.3.1 Patch 1 built on September 30, 2022</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
