📻

Jetpack ComposeでBottomNavigation を使う

2023/12/24に公開

対象者

  • Kotlin少しわかる人
  • Jetpack Compose少しわかる人

やること/やらないこと

今回は、Jetpack ComposeでBottomNavigationを作ってみようと思いましたが、パラメーターを追加するのが必須だったりして、エラーではまってとりあえず動くものを作ったので、深掘りはしていないです。

やること:

  1. 画面遷移のパッケージを追加する
    https://developer.android.com/jetpack/compose/navigation?hl=ja

  2. パッケージを追加するボタンを押す
    build.gradleにパッケージを追加したらSync Nowのボタンを押す

https://developer.android.com/jetpack/compose/navigation?hl=ja#bottom-nav

プロジェクトの説明

data classにBottomNavigationのtitleや画像のiconなどを設定します。画面遷移の設定は、rememberNavControllerで制御します。海外の動画を参考にしたのですが思ったようにうまくできませんでした😅

今回参考にした海外の動画:
https://www.youtube.com/watch?v=c8XP_Ee7iqY

全てのコード
package com.junichi.bottomnavigationapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.Email
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
// setValue, getValueを追加
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.junichi.bottomnavigationapp.ui.theme.BottomNavigationAppTheme

data class BottomNavigationItem(
    val title: String,
    val selectedIcon: ImageVector,
    val unselectedIcon: ImageVector,
    val hasNews: Boolean,
    val badgeCount: Int? = null
)

@OptIn(ExperimentalMaterial3Api::class)
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BottomNavigationAppTheme {
                // navigationを追加
                val navController = rememberNavController()
                val items = listOf(
                    BottomNavigationItem(
                        title = "Home",
                        selectedIcon = Icons.Filled.Home,
                        unselectedIcon = Icons.Outlined.Home,
                        hasNews = false,
                    ),
                    BottomNavigationItem(
                        title = "Chat",
                        selectedIcon = Icons.Filled.Email,
                        unselectedIcon = Icons.Outlined.Email,
                        hasNews = false,
                        badgeCount = 45
                    ),
                    BottomNavigationItem(
                        title = "Settings",
                        selectedIcon = Icons.Filled.Settings,
                        unselectedIcon = Icons.Outlined.Settings,
                        hasNews = true,
                    ),
                )
                // 画面遷移の状態を扱う変数を追加
                var selectedItemIndex by rememberSaveable {
                    mutableStateOf(0)
                }
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Scaffold(
                        bottomBar = {
                            NavigationBar {
                                items.forEachIndexed { index, item ->
                                    NavigationBarItem(
                                        selected = selectedItemIndex == index,
                                        onClick = {
                                            selectedItemIndex = index
                                            // ボタンを押すと画面遷移するように変更
                                             navController.navigate(item.title)
                                        },
                                        label = {
                                            Text(text = item.title)
                                        },
                                        alwaysShowLabel = false,
                                        icon = {
                                            BadgedBox(
                                                badge = {
                                                    if(item.badgeCount != null) {
                                                        Badge {
                                                            Text(text = item.badgeCount.toString())
                                                        }
                                                    } else if(item.hasNews) {
                                                        Badge()
                                                    }
                                                }
                                            ) {
                                                Icon(
                                                    imageVector = if (index == selectedItemIndex) {
                                                        item.selectedIcon
                                                    } else item.unselectedIcon,
                                                    contentDescription = item.title
                                                )
                                            }
                                        }
                                    )
                                }
                            }
                        }
                    ) {
                        // ここに画面下に表示されるBottomNavigationの画面遷移を記述
                            padding ->
                        NavHost(
                            navController = navController,
                            startDestination = items[selectedItemIndex].title
                        ) {
                            composable("Home") {
                                HomePage("Home", Modifier.padding(padding))
                            }
                            composable("Chat") {
                                EmailPage("Chat", Modifier.padding(padding))
                            }
                            composable("Settings") {
                                SettingPage("Settings", Modifier.padding(padding))
                            }
                        }
                    }
                }
            }
        }
    }
}

// 画面遷移するページを3つ作成
@Composable
fun HomePage(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Composable
fun EmailPage(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Composable
fun SettingPage(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    BottomNavigationAppTheme {
        HomePage("Android")
    }
}

こんな感じで画面を切り替えることができます。:


感想

今回は、Jetpack ComposeでBottomNavigationを作成して、よくある画面下のタブを選択するページが切り替わるUIを作ってみました!
誰かの参考になると嬉しいです。

📕こちらが完成品:
https://github.com/sakurakotubaki/JetpackBottomNavigation

Discussion