<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joel Dias</title>
    <description>The latest articles on DEV Community by Joel Dias (@joeldotdias).</description>
    <link>https://dev.to/joeldotdias</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1210451%2Fe10f6ada-8de3-4169-8eec-964978776b3c.png</url>
      <title>DEV Community: Joel Dias</title>
      <link>https://dev.to/joeldotdias</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joeldotdias"/>
    <language>en</language>
    <item>
      <title>All You Need To Know About Modal Navigation Drawers In Jetpack Compose</title>
      <dc:creator>Joel Dias</dc:creator>
      <pubDate>Wed, 15 Nov 2023 13:19:25 +0000</pubDate>
      <link>https://dev.to/joeldotdias/all-you-need-to-know-about-modal-navigation-drawers-in-jetpack-compose-pcm</link>
      <guid>https://dev.to/joeldotdias/all-you-need-to-know-about-modal-navigation-drawers-in-jetpack-compose-pcm</guid>
      <description>&lt;p&gt;We've all seen the side menu which provides us with a map of the various screens of an app. From &lt;em&gt;Gmail&lt;/em&gt; &lt;em&gt;to&lt;/em&gt; &lt;em&gt;Reddit&lt;/em&gt;, these are lifesavers when it comes to finding various functionalities. For Android developers, this seems like the go to choice for navigating within the app. You'd think there would be a resource revealing how to correctly implement this component. However, that is far from the case. I too was stuck for days trying to figure out how exactly to put this into my app but all I found was solutions that were incomplete or not optimal with a bunch of excess code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this article, I will take you through this process in a step by step manner so you can easily have this feature in your app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RrcnlKom--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700052876910/17c7eb25-af79-4800-a116-e7b8db8942f9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RrcnlKom--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700052876910/17c7eb25-af79-4800-a116-e7b8db8942f9.jpeg" alt="" width="320" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we get started, you'll need a navigation dependency in your &lt;code&gt;build.gradle&lt;/code&gt; app level file. If it isn't already present, add this &lt;code&gt;implementation("androidx.navigation:navigation-compose:2.7.5")&lt;/code&gt;&lt;br&gt;&lt;br&gt;
I have tried my best to not leave out any explanation which has resulted in the article being a tad long. Feel free to skip over the parts that you are well acquainted with.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the &lt;em&gt;Screens&lt;/em&gt; Class
&lt;/h3&gt;

&lt;p&gt;First, we'll create a sealed class containing the screens accessible from the drawer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sealed class Screens(
    val title: String,
    val route: String,
    val icon: ImageVector,
    val badgeCount: Int? = null
) {
    object Inbox: Screens(
        title = "Inbox",
        route = "inbox",
        icon = Icons.Default.Email,
        badgeCount = 31
    )
    object Sent: Screens(
        title = "Sent",
        route = "sent",
        icon = Icons.Default.Send
    )
    object Starred: Screens(
        title = "Starred",
        route = "starred",
        icon = Icons.Default.Star,
        badgeCount = 15
    )
    object Spam: Screens(
        title = "Spam",
        route = "spam",
        icon = Icons.Default.Warning
    )
    object Bin: Screens(
        title = "Bin",
        route = "bin",
        icon = Icons.Default.Delete
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have four parameters in the &lt;em&gt;Screens&lt;/em&gt; class,  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;title&lt;/em&gt;: The text that appears on the top bar of each individual screen
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;route&lt;/em&gt;: The route used while navigating to a screen
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;icon&lt;/em&gt;: The icon that appears at the side of each item in the navigation drawer
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;badgeCount&lt;/em&gt;: The text that will appear on a badge if needed. This is made nullable as not every item will need this

### Creating a NavHost to Switch Between Screens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now define the navigation of our app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun DrawerNavigation(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = Screens.Inbox.route
    ) {
        composable(Screens.Inbox.route) {
            InboxScreen()
        }
        composable(Screens.Sent.route) {
            SentScreen()
        }
        composable(Screens.Starred.route) {
            StarredScreen()
        }
        composable(Screens.Spam.route) {
            SpamScreen()
        }
        composable(Screens.Bin.route) {
            BinScreen()
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a fairly straightforward step. A &lt;em&gt;NavHost&lt;/em&gt; is created with the start destination as &lt;strong&gt;Inbox.&lt;/strong&gt; Functions are provided to navigate to each screen. If you are not familiar with this, please refer to &lt;a href="https://developer.android.com/jetpack/androidx/releases/navigation"&gt;Navigation in Jetpack Compose&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Defining the variables needed in our Composable
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val navController = rememberNavController()
val currentBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = currentBackStackEntry?.destination?.route ?: Screens.Inbox
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
val screens = listOf(
    Screens.Inbox,
    Screens.Sent,
    Screens.Starred,
    Screens.Spam,
    Screens.Bin
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First, we have a &lt;code&gt;navController&lt;/code&gt; that will be used to navigate to a screen when its item is clicked in the drawer. &lt;code&gt;currentBackStackEntry&lt;/code&gt; returns the latest back stack entry. When the navController goes to another screen, the back stack is altered, the function gets recomposed and returns the current back stack entry. &lt;code&gt;currentRoute&lt;/code&gt; stores the unique route of the latest entry onto the back stack. If it is null, it stores &lt;code&gt;Screens.Inbox&lt;/code&gt; . &lt;code&gt;drawerState&lt;/code&gt; helps us to store the current state of the drawer (open or close) while &lt;code&gt;coroutineScope&lt;/code&gt; provides us with a way to call the open and close suspend functions. &lt;code&gt;screens&lt;/code&gt; simply stores the list of screens that can be accessed from the drawer.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a Drawer Header (optional)
&lt;/h3&gt;

&lt;p&gt;You may skip this step if your navigation drawer does not need a header. I have created a very basic design. I encourage you to get as creative as possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun DrawerHeader() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 20.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        Image(
            painter = painterResource(id = R.drawable.jetpack_compose_icon),
            modifier = Modifier.size(100.dp),
            contentDescription = "App icon"
        )
        Text(
            text = "Nav\nDrawer",
            style = MaterialTheme.typography.headlineMedium,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
            color = MaterialTheme.colorScheme.onPrimaryContainer
        )
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h3&gt;
  
  
  Creating the &lt;em&gt;ModalNavigationDrawer&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, its time to actually implement it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavDrawer() {
    val navController = rememberNavController()
    val currentBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = currentBackStackEntry?.destination?.route ?: Screens.Inbox
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    val coroutineScope = rememberCoroutineScope()

    val screens = listOf(
        Screens.Inbox,
        Screens.Sent,
        Screens.Starred,
        Screens.Spam,
        Screens.Bin
    )

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                DrawerHeader()
                Divider(thickness = 1.dp, modifier = Modifier.padding(bottom = 20.dp))

                screens.forEach { screen -&amp;gt;
                    NavigationDrawerItem(
                        label = { Text(text = screen.title) },
                        icon = {
                               Icon(imageVector = screen.icon, contentDescription = "${screen.title} icon")
                        },
                        selected = currentRoute == screen.route,
                        modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
                        badge = {
                            screen.badgeCount?.let {
                                Badge(
                                    modifier = Modifier.size(30.dp),
                                    containerColor = MaterialTheme.colorScheme.primaryContainer
                                ) {
                                    Text(text = screen.badgeCount.toString())
                                }
                            }
                        },
                        onClick = {
                            navController.navigate(screen.route) {
                                launchSingleTop = true
                            }
                            coroutineScope.launch {
                                drawerState.close()
                            }
                        }
                    )
                }
            }
        }
    ) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(text = currentRoute.toString().replaceFirstChar { it.uppercase() }) },
                    modifier = Modifier.fillMaxWidth(),
                    colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer),
                    navigationIcon = {
                        IconButton(
                            onClick = {
                                coroutineScope.launch { drawerState.open() }
                            }
                        ) {
                            Icon(imageVector = Icons.Default.Menu, contentDescription = "Menu icon")
                        }
                    }
                )
            }
        ) {
            Surface(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(top = it.calculateTopPadding())
            ) {
                DrawerNavigation(navController)
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First of all, we pass &lt;code&gt;drawerState&lt;/code&gt; to store whether our drawer is open or closed. In &lt;code&gt;drawerContent&lt;/code&gt; we create a &lt;em&gt;ModalDrawerSheet&lt;/em&gt;. This is the sheet that appears when the drawer is opened. After calling &lt;em&gt;DrawerHeader,&lt;/em&gt; a &lt;em&gt;NavigationDrawerItem&lt;/em&gt; is created for each screen. Most of the parameters passed are quite self-explanatory. If &lt;code&gt;badgeCount&lt;/code&gt; is present in the screen, a badge is created displaying its value. The main talking point however, is the &lt;em&gt;onClick&lt;/em&gt; lambda. Anytime an item is clicked we navigate to the respective screen. We specify that &lt;code&gt;launchSingleTop&lt;/code&gt; is true which ensures that only one copy of our screen can be present at the top of the back stack, i.e. if an item is pressed twice or when its screen is already on top of the back stack, another copy will not be pushed onto the back stack. Besides, we call the &lt;em&gt;drawerState.close() function from our coroutineScope.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Inside the content, a &lt;em&gt;Scaffold&lt;/em&gt; is created where the &lt;code&gt;topBar&lt;/code&gt; contains the screen title and a menu icon, which when clicked, opens the drawer by calling the &lt;em&gt;drawerState.open()&lt;/em&gt; function &lt;em&gt;from our coroutineScope.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Inside the Scaffold content, &lt;em&gt;DrawerNavigation&lt;/em&gt; is called and the navController is passed to handle the &lt;em&gt;onClick&lt;/em&gt; events of the navigation items.&lt;/p&gt;

&lt;p&gt;Well, that was it. I have written this article based on how much I understand about this topic. Like all other code, even this can be further optimized. Any suggestion or corrections would be greatly appreciated.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Hope this helped you in some way.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check out the source code on &lt;a href="https://github.com/joeldotdias/ModalNavigationDrawer_Implementation.git"&gt;GitHub&lt;/a&gt;&lt;br&gt;&lt;br&gt;
You can reach out to me on &lt;a href="https://twitter.com/joeldotdias"&gt;Twitter&lt;/a&gt; or any other socials on my profile.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>androiddev</category>
      <category>jetpackcompose</category>
    </item>
  </channel>
</rss>
