Create a Modal Drawer with Jetpack Compose

Learn to create a Modal Drawer with Jetpack Compose and Kotlin

August 17, 2024
Reading Time: 5 minutes

This post will show us one way and how straightforward creating a navigation drawer using jetpack compose and Kotlin for android.

First, create a new project in android studio. Select the Empty Activity.

Empty Activity
Empty Activity

Choose your preferred name of the project and select Next.

We should be having the standard boilerplate for the project.

Now for the MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ModalDrawerTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

We will be just working on the Greeting composale.

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

Let's create these 2 needed variables.

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    ModalNavigationDrawer(drawerContent = { /*TODO*/ }) {

    }
}

Note

Always remember that when typing codes in the Android Studio, always utilize the auto code completion. With this, you won't have to worry about the import.

Inside the drawerContent object, this is where we will create a ModalDrawerSheet where we will put all the contents of our drawer.

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    ModalNavigationDrawer(
        drawerContent = {
            ModalDrawerSheet {
                Text(text = "DRAWER TITLE")

                Divider()
            }
        }) {

    }
}

Notice that the we treat the ModalDrawerSheet like a Column. So we put the contents inside as vertically aligned items. For the code above, we put in a Text for the title and a divider which is a horizontal line in vertical layouts and vertical on horizontal layout.

Now let's add a drawer item that we can select and when clicked, will take us to our desired view.

For this example, let's create 2 composable views, one for the Home and one for About. We will just put a text on each one just to demonstrate.

We will use the NavigationDrawerItem for this which takes 3 arguments: label, selected, and onClick.

  1. label - This will be the label of the drawer item.
  2. selected - A boolean value that sets if the item is selected. If true, the item will be highlighted.
  3. onClick - The code that will be fired when the item is selected.

Let's create a variable that will hold the name of the view selected.

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Open)
    val scope = rememberCoroutineScope()
    var selected = remember {
        mutableStateOf("home")
    }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Text(text = "DRAWER TITLE")

                Divider()

                // Item for home
                NavigationDrawerItem(
                    label = { Text(text = "Home") },
                    selected = selected.value == "home",
                    onClick = { selected.value = "home" })

                // Item for about
                NavigationDrawerItem(
                    label = { Text(text = "About") },
                    selected = selected.value == "about",
                    onClick = { selected.value = "about" })
            }
        }) {
        Text(text = "Content")
    }
}


 

 
 
 


 






 
 
 
 
 
 
 
 
 
 
 


 


  1. For line 3, we have set the initial value of the drawer to be Open
  2. On lines 5-6, we created a state variable of selected that is a string.
  3. For line 10, we set the value of the drawerState property to the one we defined.
  4. Then in line 13, we just created a title text. You can design it the way you want.
  5. Line 18-21 defines a navigation item. This is for the Home page and is the same as 24-27 but for the About page.
  6. For line 30, we just set a default value for the page content. Now whatever is selected in the drawer, this will not change for now.

Notice that we set the property selected to a boolean expression. This is to test if the selected page is for the assigned navigation item. Currently, we have

Preview
Preview

Next we will do is to set the page content based on the selected navigation item. Let's now do that.

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Open)
    val scope = rememberCoroutineScope()
    val selected = remember {
        mutableStateOf("home")
    }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Text(text = "DRAWER TITLE")

                Divider()

                // Item for home
                NavigationDrawerItem(
                    label = { Text(text = "Home") },
                    selected = selected.value == "home",
                    onClick = {
                        selected.value = "home"
                        scope.launch {
                            drawerState.close()
                        }
                    })

                // Item for about
                NavigationDrawerItem(
                    label = { Text(text = "About") },
                    selected = selected.value == "about",
                    onClick = {
                        selected.value = "about"
                        scope.launch {
                            drawerState.close()
                        }
                    })
            }
        }) {
        if (selected.value == "home") {
            Text(text = "Home Page")
        } else {
            Text(text = "About Page")
        }
    }
}






















 
 
 








 
 
 



 
 
 
 
 


We have minimal changes for the above code.

  1. For line 23-25 and line 34-36, this is just so that when a navigation item is clicked, the drawer will close.
  2. Then for the lines 40-44, we are just checking what is the value of selected. Since jetpack compose is reactive, the UI will update automatically.

Now do you see what is lacking? We have no way of opening the drawer! Let's work on it.

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Open)
    val scope = rememberCoroutineScope()
    val selected = remember {
        mutableStateOf("home")
    }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Text(text = "DRAWER TITLE")

                Divider()

                // Item for home
                NavigationDrawerItem(
                    label = { Text(text = "Home") },
                    selected = selected.value == "home",
                    onClick = {
                        selected.value = "home"
                        scope.launch {
                            drawerState.close()
                        }
                    })

                // Item for about
                NavigationDrawerItem(
                    label = { Text(text = "About") },
                    selected = selected.value == "about",
                    onClick = {
                        selected.value = "about"
                        scope.launch {
                            drawerState.close()
                        }
                    })
            }
        }) {
        Scaffold(
            floatingActionButton = {
                ExtendedFloatingActionButton(
                    onClick = {
                        scope.launch {
                            drawerState.open()
                        }
                    }) {
                    Icon(imageVector = Icons.Filled.Add,
                        contentDescription = "Show drawer")
                }
            }
        ) {
            Column(modifier = Modifier.padding(it)) {
                if (selected.value == "home") {
                    Text(text = "Home Page")
                } else {
                    Text(text = "About Page")
                }
            }
        }

    }
}

The code above should work and will display a floating action button at the bottom right of the screen. It is for your task to figure out and compare, what are the added codes.

To make the problem more interesting, the page contents are placed inside a Scaffolding. This way, we get the default behaviour of the floating action button.

For any questions, please feel free to ask below. Happy coding!