Hedgehogで確認したこのページの内容から加筆したものは以下です
https://bps-e.com/dev/android-004-003/
1画面のアプリであっても拡張性のためベースにNavigationを適応するようにします
参考
https://developer.android.com/jetpack/compose/navigation?hl=ja
依存関係設定
Project Structuer 設定
以下の最新を追加
androidx.navigation:navigation-compose
androidx.navigation:navigation-common-ktx
navigation-commonはNavGraphBuilderを使う場合に必要
実装
シンプルな実装例
NavHostのstartDestinationに初期画面を指定、composable()に画面UIを実装
navigate()で遷移、navigateUp()で戻る
popBackStack()で戻る場合は端末の戻るボタンと同じ動作になります
@Composable fun AppContent() {
MaterialTheme {
var navController = rememberNavController()
NavHost(navController = navController, startDestination = "top") {
composable("top") {
// 画面遷移
Button(onClick = { navController.navigate("next") }) {
Text("top")
}
}
composable("next") {
// 前の画面に戻る
Button(onClick = { navController.navigateUp() }) {
Text("next")
}
}
}
}
}
2画面以上で2画面以上戻る場合
popBackStackで遷移先を指定しinclusive/saveStateにfalseを指定します
@Composable fun AppContent() {
MaterialTheme {
var navController = rememberNavController()
NavHost(navController = navController, startDestination = "top") {
composable("top") {
Button(onClick = { navController.navigate("next") }) {
Text("top")
}
}
composable("next") {
Button(onClick = { navController.navigate("last") }) {
Text("next")
}
}
composable("last") {
// topに戻る
Button(onClick = { navController.popBackStack(route = "top", inclusive = false, saveState = false) }) {
Text("last")
}
}
}
}
}
NavGraphBuilderを使用する場合
NavGraphBuilderを拡張して実装します
fun NavGraphBuilder.topGraph() {
composable(route = "top") {
...
}
}
fun NavGraphBuilder.nextGraph() {
composable(route = "next") {
...
}
}
@Composable fun App() {
...
NavHost(navController = navController, startDestination = "top") {
topGraph()
nextGraph()
}
}
実際の実装例
navControllerを全体をコンポーザブルに直接渡すとテストや再利用時しにくくなるため、トリガーするナビゲーション アクションを定義するコールバックを使います
また以下のようにパターン化した実装にすることで画面の実装に集中できます
(NavRouteに項目増やしてScreenを実装してNavGrapにセットするだけ)
@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 = "") {
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) -> Unit, onNavigate: (String) -> Unit) {
composable(route = route.name) {
when(route) {
NavRoute.Top -> TopScreen(onNavigate = { onNavigate(it) })
NavRoute.Next -> NextScreen(onBack = { onBack(it) })
}
}
}
@Composable fun AppNavHost(navController: NavHostController, onBack: (String) -> Unit, onNavigate: (String) -> Unit) {
NavHost(navController = navController, startDestination = NavRoute.Top.name) {
NavRoute.values().forEach { navGraph(it, onBack, onNavigate) }
}
}
// TopScreen.kt
@Composable fun TopScreen(onNavigate: (String) -> Unit) {
Button(onClick = { onNavigate(NavRoute.Next.name) }) {
Text(NavRoute.Top.name)
}
}
// NextScreen.kt
@Composable fun NextScreen(onBack: (String) -> Unit) {
Button(onClick = { onBack("") }) {
Text(NavRoute.Next.name)
}
}
データを渡す
navigateの引数に文字列で値を渡します
使いづらい…
この実装の場合はパラメータ無し(空文字)の場合Exceptionが発生します
@OptIn(ExperimentalMaterial3Api::class)
@Composable fun AppContent() {
MaterialTheme {
var navController = rememberNavController()
NavHost(navController = navController, startDestination = "top") {
composable("top") {
var text by remember { mutableStateOf("") }
Column {
TextField(value = text, onValueChange = { text = it })
Button(onClick = {
navController.navigate("next/${text}/1")
}) {
Text("top")
}
}
}
composable("next/{text}/{id}",
arguments = listOf(
navArgument("text") { type = NavType.StringType },
navArgument("id") { type = NavType.IntType },
)
) {
Column {
val text = it.arguments?.getString("text") ?: ""
val id = it.arguments?.getInt("id") ?: 0
Text("${text}_${id}")
Button(onClick = { navController.navigateUp() }) {
Text("next")
}
}
}
}
}
}
引数省略可能にする場合は以下のようにします
composable("top") {
...
Button(onClick = {
navController.navigate("next?text=${text}&id=1")
}) {
...
}
}
composable("next?text={text}&id={id}",
arguments = listOf(
navArgument("text") {
type = NavType.StringType
defaultValue = ""
},
navArgument("id") {
type = NavType.IntType
defaultValue = 0
}
)
) {
...
}
Android Studio Dolphin 2021.3.1 Patch 1 built on September 30, 2022