Bottom Navigation with Navigation Component in Jetpack Compose

Nanda Adisaputra
10 min readNov 16, 2023

--

Berjumpa lagi dengan Saya Nanda Adisaputra, S.Kom. Pada kesempatan kali ini Saya akan sharing bagaimana cara membuat Bottom Navigation menggunakan modern UI yaitu Jetpack Compose untuk pemula.

The Jetpack Compose logo used in this image is the official logo created by Google

Jetpack Compose merupakan framework untuk mendesain UI dengan pendekatan declarative yang cepat, mudah, simpel dan kode yang dihasilkan akan lebih sedikit karena cukup perlu menggunakan bahasa pemprograman kotlin saja.

Kenapa perlu Jetpack Compose ?

  1. Lebih sedikit code
  2. Cukup hanya menggunakan bahasa pemprograman kotlin tanpa XML
  3. Deklaratif yang Intuitif
  4. Kecil dan Idependen
  5. Separation of Concern(SoC)
  6. Interoperability dengan XML
  7. Powerful API & Tool

Nah menarik bukan, yuk langsung saja Kita bahas.

  1. Langkah pertama mari Kita menambahkan terlebih dahulu library Navigation Component pada build.gradle(Module: app) seperti berikut.
//navigation
implementation "androidx.navigation:navigation-compose:2.7.5"

Jangan lupa untuk Sync project yang terletak pada pojok kanan atas untuk mengunduh library tersebut.

2. Selanjutnya Kita akan Kemudian Kita akan menyiapkan package baru dengan nama data didalam package ui dengan cara klik kanan pada package semarangtourism->New -> Package seperti pada gambar dibawah ini.

3. Didalam package data Kita akan siapkan juga package constant dengan cara klik kanan pada package data -> New -> Package.

4. Selanjutnya didalam package constant, Kita akan membuat class baru dengan nama Const dengan isi data cons seperti berikut ini.

class Const {
object Cons {
const val HOME ="home"
const val FAVORITE ="favorite"
const val ABOUT ="about"
const val DETAIL ="home/{id}"
}
}

5. Kemudian Kita akan menyiapkan package baru dengan nama navigation didalam package ui dengan cara klik kanan pada package ui -> New -> Package.

6. Selanjutnya Kita akan membuat sealed class baru dengan nama Screen.kt dengan isi nama nama halaman beserta routenya seperti berikut ini.

sealed class Screen(val route: String) {
object Home : Screen(Const.Cons.HOME)
object Favorite : Screen(Const.Cons.FAVORITE)
object AboutMe : Screen(Const.Cons.ABOUT)
object Detail : Screen(Const.Cons.DETAIL) {
fun createRoute(tourismId: Int) = "home/$tourismId"
}

}

7. Kemudian di package navigation, Kita akan membuat class baru lagi dengan nama NavigationItem.kt dengan isi sebagai berikut.

import androidx.compose.ui.graphics.painter.Painter

data class NavigationItem(
val screen: Screen,
val title: String,
val icon: Painter
)

8. Kemudian di package semarangtourism, Kita akan membuat class bernama SemarangTourismApp dan Kita pindahkan MainActivity.kt ke dalam package ui dengan cara drag and drop.

Sehingga struktur project nya akan menjadi seperti pada gambar berikut ini.

9. Selanjutnya didalam strings.xml yang terletak pada res->values->strings.xml tambahkan beberapa resource string seperti berikut ini.

<resources>
<string name="app_name">SemarangTourism</string>
<string name="menu_home">Home</string>
<string name="menu_favorite">Favorite</string>
<string name="menu_about">About</string>
</resources>

10. Kemudian tambahkan beberapa icon didalam res-> drawable dengan cara klik kanan pada package drawable -> New -> Vector Asset -> klik icon Clip Art -> Search nama icon -> pilih icon -> Klik Ok.

11. Kemudian untuk memudahkan dalam mengenali dan membacanya Kita rename Name nya dengan awalan ic_ contoh: ic_home, ic_favorite, ic_about, dan seterusnya. Setelah itu Kita klik Next -> Finish.

12. Lakukan hal yang sama untuk menambahkan icon favorite dan icon person. Sehingga hasilnya akan seperti berikut ini.

13. Didalam class SemarangTourismApp.kt , buat Composable baru untuk menampilkan bottom bar seperti berikut.

@Composable
private fun BottomBar(
modifier: Modifier = Modifier
) {
BottomNavigation(
modifier = modifier,
) {
val navigationItems = listOf(
NavigationItem(
title = stringResource(R.string.menu_home),
icon = painterResource(id = R.drawable.ic_home),
screen = Screen.Home
),
NavigationItem(
title = stringResource(R.string.menu_favorite),
icon = painterResource(id = R.drawable.ic_favorite),
screen = Screen.Favorite
),
NavigationItem(
title = stringResource(R.string.menu_about),
icon = painterResource(id = R.drawable.ic_person),
screen = Screen.AboutMe
),
)
navigationItems.map { item ->
BottomNavigationItem(
icon = {
Icon(
painter = item.icon,
contentDescription = item.title,
modifier = Modifier
.size(24.dp)
)
},
label = {
Text(
text = item.title,
fontSize = 11.sp
)
},
selected = false,
onClick = {
}
)
}
}
}

14. Selanjutnya, Kita akan menampilkan BottomBar di dalam fungsi SemarangTourismApp menggunakan Scaffold.

@Composable
fun SemarangTourismApp(
modifier: Modifier = Modifier
) {
BottomNavigation(
modifier = modifier
) {
Scaffold(
bottomBar = {
BottomBar()
},
modifier = modifier
) { innerPadding ->

{

}
}
}
}

Sehingga keseluruhan isi dari class SemarangTourismApp.kt akan seperti berikut ini.

import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nandaadisaputra.semarangtourism.ui.navigation.NavigationItem
import com.nandaadisaputra.semarangtourism.ui.navigation.Screen

@Composable
fun SemarangTourismApp(
modifier: Modifier = Modifier
) {
BottomNavigation(
modifier = modifier
) {
Scaffold(
bottomBar = {
BottomBar()
},
modifier = modifier
) { innerPadding ->

{

}
}
}
}

@Composable
private fun BottomBar(
modifier: Modifier = Modifier
) {
BottomNavigation(
modifier = modifier,
) {
val navigationItems = listOf(
NavigationItem(
title = stringResource(R.string.menu_home),
icon = painterResource(id = R.drawable.ic_home),
screen = Screen.Home
),
NavigationItem(
title = stringResource(R.string.menu_favorite),
icon = painterResource(id = R.drawable.ic_favorite),
screen = Screen.Favorite
),
NavigationItem(
title = stringResource(R.string.menu_about),
icon = painterResource(id = R.drawable.ic_person),
screen = Screen.AboutMe
),
)
navigationItems.map { item ->
BottomNavigationItem(
icon = {
Icon(
painter = item.icon,
contentDescription = item.title,
modifier = Modifier
.size(24.dp)
)
},
label = {
Text(
text = item.title,
fontSize = 11.sp
)
},
selected = false,
onClick = {
}
)
}
}
}

15. Kemudian pada class MainActivity.kt modifikasi source codenya hingga menjadi seperti dibawah ini.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.SnackbarDefaults.backgroundColor
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.nandaadisaputra.semarangtourism.SemarangTourismApp
import com.nandaadisaputra.semarangtourism.ui.theme.SemarangTourismTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SemarangTourismTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = backgroundColor
) {
SemarangTourismApp()
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun SemarangTourismAppPreview() {
SemarangTourismTheme {
SemarangTourismApp()
}
}

16. Silahkan running aplikasi SemarangTourism sehingga tampilannya akan seperti dibawah ini.

17. Langkah berikutnya Kita akan mengintegrasikan BottomBar dengan Navigation Component dengan cara membuat terlebih dahulu variabel navController sebagai parameter pada BottomBar seperti berikut.

//@Composable
//fun SemarangTourismApp(
......................
navController: NavHostController = rememberNavController(),
) {
...................
// Scaffold(
// bottomBar = {
BottomBar(navController)
// },
................................
// ) { innerPadding ->
..................
...............
}

//@Composable
//private fun BottomBar(
navController: NavHostController,
............................
) {
................
..................
}

18. Langkah selanjutnya Kita akan menambahkan NavHost didalam Scaffold yang akan digunakan sebagai container untuk halaman yang lainnya.

.......................
........................
// modifier = modifier
// ) { innerPadding ->
NavHost(
navController = navController,
//dashboard utama
startDestination = Screen.Home.route,
modifier = Modifier
.padding(innerPadding)
) {
composable(Screen.Home.route) {
HomeScreen()
}
composable(Screen.Favorite.route) {
FavoriteScreen()
}
composable(Screen.AboutMe.route) {
AboutScreen()
}
}
}
}
}
//@Composable
//private fun BottomBar(
// navController: NavHostController,
// modifier: Modifier = Modifier
//) {
........................
.........................
//}

Kalau dilihat pada HomeScreen(), FavoriteScreen(), dan AboutScreen() masih merah, karena Kita belum membuat class HomeScreen.kt, FavoriteScreen.kt, dan AboutScreen.kt

19. Sehingga Kita perlu membuat class HomeScreen.kt, FavoriteScreen.kt, dan AboutScreen.kt dan setiap classnya Kita masukkan kedalam package dengan cara sebagai berikut.

Klik kanan pada ui -> New -> Package -> berinama package dengan nama home.

Klik kanan pada ui -> New -> Package -> berinama package dengan nama favorite.

Klik kanan pada ui -> New -> Package -> berinama package dengan nama about.

20. Kemudian Kita tambahkan class ke masing-masing package dengan cara klik kanan pada package home -> New -> Kotlin Class / File

21. Langkah selanjutnya pada class HomeScreen.kt Kita isikan dengan source code seperti berikut.

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nandaadisaputra.semarangtourism.ui.theme.SemarangTourismTheme

@Composable
fun HomeScreen() {
SemarangTourismTheme {
Surface(
modifier = Modifier
.fillMaxSize()
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically),
color = MaterialTheme.colors.background,
) {
Greeting("Home")
}
}
}

@Composable
fun Greeting(name: String) {
Text(
text = "Ini Halaman $name!",
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
modifier = Modifier
.clickable { }
.background(Color.White)
.padding(8.dp)
.border(2.dp, Color.Blue)
.padding(8.dp),
color = Color.Blue,

textAlign = TextAlign.Center
)
}

@Preview(showBackground = true)
@Composable
fun HomeContentPreview() {
SemarangTourismTheme {
Greeting("Home")
}
}

22. Kemudian pada class FavoriteScreen.kt Kita isikan dengan source code seperti berikut.

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nandaadisaputra.semarangtourism.ui.theme.SemarangTourismTheme

@Composable
fun FavoriteScreen() {
SemarangTourismTheme {
Surface(
modifier = Modifier
.fillMaxSize()
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically),
color = MaterialTheme.colors.background,
) {
Greeting("Favorite")
}
}
}

@Composable
fun Greeting(name: String) {
Text(
text = "Ini Halaman $name!",
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
modifier = Modifier
.clickable { }
.background(Color.White)
.padding(8.dp)
.border(2.dp, Color.Blue)
.padding(8.dp),
color = Color.Blue,

textAlign = TextAlign.Center
)
}

@Preview(showBackground = true)
@Composable
fun FavoriteContentPreview() {
SemarangTourismTheme {
Greeting("Favorite")
}
}

23. Kemudian pada class AboutScreen.kt Kita isikan dengan source code seperti berikut.

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nandaadisaputra.semarangtourism.ui.theme.SemarangTourismTheme


@Composable
fun AboutScreen() {
SemarangTourismTheme {
Surface(
modifier = Modifier
.fillMaxSize()
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically),
color = MaterialTheme.colors.background,
) {
Greeting("About")
}
}
}

@Composable
fun Greeting(name: String) {
Text(
text = "Ini Halaman $name!",
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
modifier = Modifier
.clickable { }
.background(Color.White)
.padding(8.dp)
.border(2.dp, Color.Blue)
.padding(8.dp),
color = Color.Blue,

textAlign = TextAlign.Center
)
}

@Preview(showBackground = true)
@Composable
fun AboutContentPreview() {
SemarangTourismTheme {
Greeting("About")
}
}

Sehingga ketika Kita running aplikasinya hasilnya akan seperti berikut ini.

Akan tetapi ketika icon lain dipilih, tidak terjadi apa-apa karena kita masih belum mengintegrasikannya.

24. Untuk mengintegrasikan tombol, tambahkan aksi berikut ketika NavigationBarItem ditekan.

//)
// },
// selected = false,
onClick = {
navController.navigate(item.screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
restoreState = true
launchSingleTop = true
}
}
// )
// }
// }
// }

Berikut beberapa hal yang perlu Kita ketahui dari kode di atas.

  • navController.navigate: untuk eksekusi navigasi ke route sesuai dengan item yang dipilih.
  • popUpTo: untuk kembali ke halaman awal supaya tidak membuka halaman baru terus menerus.
  • saveState dan restoreState: mengembalikan state ketika item dipilih lagi.
  • launchSingleTop: digunakan supaya tidak ada halaman yang dobel ketika memilih ulang item yang sama.

25. Selanjutnya , Kita akan mengatur tampilan Bottom Navigation sehingga terdapat parameter select true atau false menggunakan currentBackStackEntryAsState.

................
import androidx.compose.runtime.getValue
..............

//@Composable
//private fun BottomBar(
// navController: NavHostController,
// modifier: Modifier = Modifier
//) {
// BottomNavigation(
// modifier = modifier,
// ) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

.................
// icon = painterResource(id = R.drawable.ic_home),
// screen = Screen.Home
// ),
..............
..........................
selected = currentRoute == item.screen.route,
//onClick = {
..............
.....................
}

26. Sehingga keseluruhan source code pada SemarangTourismApp.kt sebagai berikut.

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.nandaadisaputra.semarangtourism.ui.about.AboutScreen
import com.nandaadisaputra.semarangtourism.ui.favorite.FavoriteScreen
import com.nandaadisaputra.semarangtourism.ui.home.HomeScreen
import com.nandaadisaputra.semarangtourism.ui.navigation.NavigationItem
import com.nandaadisaputra.semarangtourism.ui.navigation.Screen

@Composable
fun SemarangTourismApp(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
) {
BottomNavigation(
modifier = modifier
) {
Scaffold(
bottomBar = {
BottomBar(navController)
},
modifier = modifier
) { innerPadding ->
NavHost(
navController = navController,
startDestination = Screen.Home.route,
modifier = Modifier
.padding(innerPadding)
) {
composable(Screen.Home.route) {
HomeScreen()
}
composable(Screen.Favorite.route) {
FavoriteScreen()
}
composable(Screen.AboutMe.route) {
AboutScreen()
}
}
}
}
}

@Composable
private fun BottomBar(
navController: NavHostController,
modifier: Modifier = Modifier
) {
BottomNavigation(
modifier = modifier,
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

val navigationItems = listOf(
NavigationItem(
title = stringResource(R.string.menu_home),
icon = painterResource(id = R.drawable.ic_home),
screen = Screen.Home
),
NavigationItem(
title = stringResource(R.string.menu_favorite),
icon = painterResource(id = R.drawable.ic_favorite),
screen = Screen.Favorite
),
NavigationItem(
title = stringResource(R.string.menu_about),
icon = painterResource(id = R.drawable.ic_person),
screen = Screen.AboutMe
),
)
navigationItems.map { item ->
BottomNavigationItem(
icon = {
Icon(
painter = item.icon,
contentDescription = item.title,
modifier = Modifier
.size(24.dp)
)
},
label = {
Text(
text = item.title,
fontSize = 11.sp
)
},
selected = currentRoute == item.screen.route,
onClick = {
navController.navigate(item.screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
restoreState = true
launchSingleTop = true
}
}
)
}
}
}

27. Selanjutnya Kita running Aplikasinya maka akan seperti berikut hasilnya.

Nah akhirnya, Kita telah berhasil membuat Bottom Navigation menggunakan Jetpack Compose.

Source code lengkapnya dapat dilihat dilink github Saya : https://github.com/NandaAdisaputra/DevFest2023.git

Sekian tutorial singkat dari Saya. Jika ada pertanyaan silahkan tinggal kan kalimat di kolom komentar, semoga bermanfaat…

--

--

No responses yet