Jetpack Composeを使ってみたい⑤ Navigation
Jetpack Composeを使ってみたい!と思ってCodelabを元に勉強してみた記録。
Jetpack Compose for Android Developers
JetpackComposeでNavigationコンポーネントを使って画面遷移を実装する。
準備
アプリレベルのbuild.gradleに依存関係を追加。
dependencies { def nav_version = "2.5.2" implementation "androidx.navigation:navigation-compose:$nav_version" }
基本的なNavigation
アプリ内の画面を構成するコンポーザブルのバックスタックと各画面の状態を追跡するNavControllerを作成する。
他のコンポーザブルから参照できるよう上位のコンポーザブルに定義する。
val navController = rememberNavController()
ナビゲーショングラフを関連付けるためのコンテナであるNavHostをNavControllerに関連付ける。
NavHostにはNavControllerと最初の画面になるコンポーザブルのルートを渡す。
(ルートはコンポーザブルへのパスを定義するStringで、コンポーザブルごとに一意)
ナビゲーショングラフには目的地となるコンポーザブルを指定する。
NavHost(navController = navController, startDestination = "profile") { composable(route = "profile") { Profile(/*...*/) } composable(route = "friendslist") { FriendsList(/*...*/) } /*...*/ } //目的地になるコンポーザブルは別で作成 @Composable fun Profile(){ } @Composable fun FriendList(){ }
目的地に移動するときは各コンポーザブルに設定したルートを使用する。
navController.navigate("friendslist")
引数を使用したNavigation
コンポーザブルに引数を渡したい時は、ルートにプレースホルダーを追加する。
引数のタイプはデフォルトで文字列になっているが、argumentsを使用して明確に指定できる。
引数の値は、composable() 関数のラムダで使用可能なNavBackStackEntry から抽出する。
composable( "profile/{userId}", arguments = listOf(navArgument("userId") { type = NavType.StringType }) ){ backStackEntry -> Profile(backStackEntry.arguments?.getString("userId")) } //引数を受け取るコンポーザブル @Composable fun FriendList(userId: String){ }
サンプルアプリ
HomeScreen ⇄ NextScreen の2つのScreenを遷移するアプリ。
HomeScreenはTextFieldに入力された文字を引数としてNextScreenに渡し、
NextScreenは受け取った文字列を表示する。
画面を管理するenum class
enum class NavigationScreen(){ Home, Next }
NavControllerを持つComposable
@Composable fun NavigationApp() { val navController = rememberNavController() val backStackEntry by navController.currentBackStackEntryAsState() Scaffold( topBar = { //この後アプリバーを作成 MyAppBar( canNavigateBack = backStackEntry?.destination?.route != NavigationScreen.Home.name, onClick = { navController.popBackStack() } ) } ) { innerPadding -> NavHost( navController = navController, startDestination = NavigationScreen.Home.name, modifier = Modifier.padding(innerPadding) ) { composable(NavigationScreen.Home.name) { HomeScreen( //NavContorollerを直接渡さず、コールバックで渡すのが良さそう onClick = { navController.navigate("${NavigationScreen.Next.name}/$it") } ) } composable( "${NavigationScreen.Next.name}/{text}", arguments = listOf(navArgument("text") { type = NavType.StringType }) ) { NextScreen(it.arguments?.getString("text").toString()) } } } }
アプリバー
@Composable fun MyAppBar( canNavigateBack: Boolean, onClick:() -> Unit ){ //currentDestinationがHomeScreenじゃなければアプリバーに戻るボタンを表示 val navigationIcon: (@Composable () -> Unit)? = { if (canNavigateBack) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Outlined.ArrowBack, contentDescription = "Back" ) } } else { null } } TopAppBar( title = { Text(text = stringResource(id = R.string.app_name)) }, navigationIcon = navigationIcon ) }
HomeScreen
@Composable fun HomeScreen( onClick: (String) -> Unit ) { var name by remember { mutableStateOf("") } Column( modifier = Modifier .fillMaxSize() .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Welcome Home!") Spacer(modifier = Modifier.padding(16.dp)) OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text(text = "Enter your name") }, singleLine = true ) Button( onClick = { onClick(name) } ) { Text(text = "Next") } } }
NextScreen
@Composable fun NextScreen(text: String) { Column( modifier = Modifier .fillMaxSize() .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Hello, $text") } }
参考
Jetpack Compose Navigation | Android Developers
Compose を使用したナビゲーション | Jetpack Compose | Android Developers
前の記事
mtnmr.hatenablog.com