Basic Android Compose - Navigation Drawer Menu
Hello! I will share how to implement a navigation drawer menu from scratch using Jetpack Compose. Without further ado, let’s begin!
Step 1
Select the Empty Compose Activity
with New Project
option from Android Studio.
Step 2
Create .../data
& .../composables
packages next to MainAcitivy.kt
.
Step 3
Add the navigation compose dependency in the build.gradle (:app)
. This dependency enables Jetpack Navigation through Jetpack Compose.
dependencies {
// other dependencies...
implementation "androidx.navigation:navigation-compose:2.5.2"
}
Step 4
Add the following strings to strings.xml
.
<resources>
<!-- other strings... -->
<string name="screen_1">Screen 1</string>
<string name="screen_2">Screen 2</string>
<string name="screen_3">Screen 3</string>
</resources>
Step 5
You need a data class to hold the menu items. Create .../data/MenuItem.kt
.
// package ...
// use your package
// import ....R
data class MenuItem(
val id: ScreensRoute,
val textId: Int
)
val drawerScreens = listOf(
MenuItem(ScreensRoute.SCREEN_1, R.string.screen_1),
MenuItem(ScreensRoute.SCREEN_2, R.string.screen_2),
MenuItem(ScreensRoute.SCREEN_3, R.string.screen_3),
)
enum class ScreensRoute {
SCREEN_1, SCREEN_2, SCREEN_3
}
Step 6
Create .../composables/DrawerItem.kt
.
// package ....
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
// use your package
// import ....data.MenuItem
@Composable
fun DrawerItem(menuItem: MenuItem, modifier: Modifier = Modifier, onItemClick: (MenuItem) -> Unit) {
Column(
modifier = Modifier.clickable {
onItemClick(menuItem)
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.padding(8.dp)
) {
Text(
text = stringResource(id = menuItem.textId),
fontSize = 25.sp,
modifier = Modifier.padding(horizontal = 10.dp)
)
}
Divider()
}
}
Step 7
Create .../composables/DrawerBody.kt
.
// package ...
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
// use your package
// import ....data.MenuItem
@Composable
fun DrawerBody(
menuItems: List<MenuItem>,
scaffoldState: ScaffoldState,
scope: CoroutineScope,
modifier: Modifier = Modifier,
onItemClick: (MenuItem) -> Unit
) {
LazyColumn(
modifier = modifier
) {
items(menuItems) { item ->
DrawerItem(
item,
modifier = modifier
) {
scope.launch {
scaffoldState.drawerState.close()
}
onItemClick(item)
}
}
}
}
Step 8
In the .../composables
package, create TopBar.kt
, NavHost.kt
and ScreenContent.kt
.
TopBar
: This is for the top bar on the screen.NavHost
: NavHost is used to navigate between the screen.ScreenContent
: The content on the screen.
TopBar.kt
// package ...import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp@Composable
fun TopBar(
titleResId: Int,
modifier: Modifier = Modifier,
openDrawer: () -> Unit
) {
TopAppBar(
title = {
Text(text = stringResource(id = titleResId))
},
navigationIcon = {
Icon(
imageVector = Icons.Default.Menu,
modifier = Modifier
.padding(start = 8.dp)
.clickable {
openDrawer()
},
contentDescription = null
)
},
modifier = modifier
)
}
ScreenContent.kt
// package ...import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource@Composable
fun ScreenContent(titleId: Int, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = stringResource(id = titleId), style = MaterialTheme.typography.h4)
}
}
NavHost.kt
// package ...import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
// use your package
// import ...R
// import ....data.ScreensRoute@Composable
fun NavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = ScreensRoute.SCREEN_1.name
) {
composable(ScreensRoute.SCREEN_1.name) {
ScreenContent(R.string.screen_1)
}
composable(ScreensRoute.SCREEN_2.name) {
ScreenContent(R.string.screen_2)
}
composable(ScreensRoute.SCREEN_3.name) {
ScreenContent(R.string.screen_3)
}
}
}
Step 9
Use TopBar
, DrawerBody
wrap in Scaffold
component, and NavHost
in MainActivity.kt
.
// package ...
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.launch
// use your package
// import ...R
// import ...composables.DrawerBody
// import ...composables.MyNavHost
// import ...composables.MyTopAppBar
// import ...data.drawerScreens
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DrawerNavigationScreen()
}
}
}
@Composable
fun DrawerNavigationScreen() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
val navController = rememberNavController()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopBar(
titleResId = R.string.app_name,
openDrawer =
{
scope.launch {
// Open the drawer with animation
// and suspend until it is fully
// opened or animation has been canceled
scaffoldState.drawerState.open()
}
}
)
},
drawerGesturesEnabled = true,
drawerContent = {
DrawerBody(
menuItems = drawerScreens,
scaffoldState,
scope
) {
navController.navigate(it.id.name) {
popUpTo = navController.graph.startDestinationId
launchSingleTop = true
}
}
}
) {
NavHost(navController = navController)
}
}
And this is it! You should have this outcome.
If you liked the content, please clap 👏 and leave me feedback so I can enhance the quality of the content in my following articles. Thank you for your time!
Deuk Services: Your Gateway to Leading Android Innovation
Are you looking to boost your business with top-tier Android solutions?Partner with Deuk services and take your projects to unparalleled heights.