Creating a DropDown Menu in Jetpack Compose

Michael Ojiakor
6 min readJun 20, 2024

--

Photo by Nathan Dumlao on Unsplash

Using a DropDown menu in the traditional Android Views system involves utilizing the Spinner widget along with an ArrayAdapter. In contrast, in Jetpack Compose, it utilizes the DropdownMenu and DropdownMenuItem composables, embracing a declarative approach.

Jetpack Compose, Google’s modern toolkit for building native Android UIs, simplifies UI development with its declarative approach. A common UI component in many applications is the DropDown menu, allowing users to select an option from a list. This guide will walk you through creating a DropDown menu in Jetpack Compose.

Creating the DropDown Menu

To create a DropDown menu in Jetpack Compose, you would utilize two components: the DropdownMenu and the DropdownMenuItem. The DropdownMenu is responsible for displaying the options, while the DropdownMenuItem represents each selectable item.

Let’s proceed by defining a function named DropDownMenuDemo in our project file. Within this function, we’ll declare the DropdownMenu, DropdownMenuItem, and other necessary components for our app.


@Composable
fun DropDownDemo() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text("This is a text")
Image(
painter = painterResource(id = R.drawable.drop_down_ic),
contentDescription = "DropDown Icon"
)
}
}
}
Output

In the code above, we arranged our components using rows and columns. We also added a drawable vector asset to indicate where we want the dropdown menu. Here’s a snippet of the vector asset description. We’ve changed the name to “drop_down_ic,” but you can tweak it to whatever name you prefer.

The DropdownMenu composable in Jetpack Compose takes two key parameters:

1. expanded: A boolean that controls the visibility of the menu.
2. onDismissRequest: A lambda function that defines the actions to take when the menu is dismissed.

These parameters ensure the menu’s interactive and controlled display behavior.

Define State Variables

To manage the dropdown menu’s visibility, we’ll use a state variable. This variable will track whether the menu is expanded (open) or collapsed (closed).

   val isDropDownExpanded = remember {
mutableStateOf(false)
}

We then pass this state variable to our Dropdown component as the value for its “expanded” property.

   DropdownMenu(
expanded = isDropDownExpanded.value,
onDismissRequest = {
isDropDownExpanded.value = false
}) {
}

Don’t worry if this doesn’t make sense yet. We’ll provide the full code soon. For now, let’s focus on the basics. To dismiss the dropdown menu, we set the “isDropDownExpanded” value to “false” in the “onDismissRequest” function. This guide emphasizes learning the fundamentals, so we’ll keep the code simple. We’ll design the app to let you select a name from a list. Once you understand these basics, you can tweak the code for your specific needs.

 val usernames = listOf("Alexander", "Isabella", "Benjamin", "Sophia", "Christopher")

We would need a state variable to monitor the selected option position. We would set the initial value as “0”.

val itemPosition = remember {
mutableStateOf(0)
}

Now that we are done with this, let’s move on to the next step.

Define the DropDown Menu

The “DropdownMenuItem” composable in Jetpack Compose takes two key parameters:

  1. text: A composable lambda that specifies the content to display within the menu item, typically a “Text” composable.
  2. onClick: A lambda function that defines the actions to take when the menu item is clicked, such as updating state or closing the menu.

We have a list of names we want to display in our dropdown, we would use the “forEachIndexed” function to loop through to list of names and display them as out our dropdown item.

Using the “forEachIndexed” function to create multiple “DropDownMenuItem” composables in Jetpack Compose allows you to iterate through the “usernames” with access to both the index and the value (username) in a concise and readable manner. This approach simplifies the creation of menu items by dynamically generating them based on the contents of the list, ensuring scalability and reducing repetitive code. The advantage is that it automatically handles the positioning and uniqueness of each item, making the code easier to maintain and update.

 usernames.forEachIndexed { index, username ->
DropdownMenuItem(text = {
Text(text = username)
},
onClick = {
isDropDownExpanded.value = false
itemPosition.value = index
})
}

In this code, each “DropdownMenuItem” composable displays a username from the “usernames” list and, upon being clicked, closes the dropdown menu and updates the selected item’s position using “forEachIndexed” to handle these actions dynamically.

Complete Code

To finish of, since we are reading the usernames from a list, we update other parts of our code that need to be updated dynamically.

NB: Wrapping the code inside a container like a “Box” is necessary to control the positioning of the dropdown menu relative to its parent or surrounding elements. This ensures that the dropdown displays where intended, without being obscured or positioned incorrectly based on the layout constraints of its parent or the overall UI. It allows for more precise control over the dropdown’s positioning, ensuring a consistent and user-friendly experience across different screen sizes and orientations.

We also make the Row clickable by specifying it in the modifier. Making the Row clickable with a modifier like Modifier.clickable ensures that the entire row area is interactive, covering every other UI element within it. When the row is clicked, the isDropDownExpanded value is set to true, which triggers the visibility of the dropdown menu elsewhere in the code. This approach encapsulates the behavior of showing the dropdown menu within the row’s clickable area, providing a clear and intuitive interaction for users. The full code is given below:

package com.example.dropdownexample

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.example.dropdownexample.ui.theme.DropDownExampleTheme

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

@Composable
fun DropDownDemo() {

val isDropDownExpanded = remember {
mutableStateOf(false)
}

val itemPosition = remember {
mutableStateOf(0)
}

val usernames = listOf("Alexander", "Isabella", "Benjamin", "Sophia", "Christopher")

Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {

Box {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable {
isDropDownExpanded.value = true
}
) {
Text(text = usernames[itemPosition.value])
Image(
painter = painterResource(id = R.drawable.drop_down_ic),
contentDescription = "DropDown Icon"
)
}
DropdownMenu(
expanded = isDropDownExpanded.value,
onDismissRequest = {
isDropDownExpanded.value = false
}) {
usernames.forEachIndexed { index, username ->
DropdownMenuItem(text = {
Text(text = username)
},
onClick = {
isDropDownExpanded.value = false
itemPosition.value = index
})
}
}
}

}
}

@Preview(showBackground = true)
@Composable
fun DropDownDemoPreview() {
DropDownExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
DropDownDemo()
}

}
}
Ouput
Output
Output

Conclusion

With Jetpack Compose, creating a DropDown menu is straightforward and highly customizable. By leveraging Compose’s declarative syntax, you can build interactive and visually appealing UIs efficiently. Experiment with different styles and configurations to fit your application’s needs.

Happy coding!

--

--