# ExposedDropdownMenuBox

Common/AllAndroid/JVM

## Functions summary

|---|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `@https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenuBox.composable#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)( expanded: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/index.html, onExpandedChange: (https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/index.html) -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html, modifier: https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier, content: @https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenuBoxScope.() -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html )` [Material Design exposed dropdown menu](https://m3.material.io/components/menus/overview) | Cmn |

## Functions

### ExposedDropdownMenuBox

Cmn Artifact: [androidx.compose.material3:material3](https://developer.android.com/jetpack/androidx/releases/compose-material3) [View Source](https://cs.android.com/search?q=file:androidx/compose/material3/ExposedDropdownMenu.kt+function:ExposedDropdownMenuBox)

```
@Composable
fun ExposedDropdownMenuBox(
    expanded: Boolean,
    onExpandedChange: (Boolean) -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable ExposedDropdownMenuBoxScope.() -> Unit
): Unit
```

[Material Design exposed dropdown menu](https://m3.material.io/components/menus/overview)

Menus display a list of choices on a temporary surface. They appear when users interact with a button, action, or other control.

Exposed dropdown menus, sometimes also called "spinners" or "combo boxes", display the currently selected item in a text field to which the menu is anchored. In some cases, it can accept and display user input (whether or not it's listed as a menu choice), in which case it may be used to implement autocomplete.

![Exposed dropdown menu
image](https://developer.android.com/images/reference/androidx/compose/material3/exposed-dropdown-menu.png)

The `https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenuBox.composable#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)` is expected to contain a `https://developer.android.com/reference/kotlin/androidx/compose/material3/TextField.composable#TextField(androidx.compose.foundation.text.input.TextFieldState,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.text.TextStyle,androidx.compose.material3.TextFieldLabelPosition,kotlin.Function1,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Boolean,androidx.compose.foundation.text.input.InputTransformation,androidx.compose.foundation.text.input.OutputTransformation,androidx.compose.foundation.text.KeyboardOptions,androidx.compose.foundation.text.input.KeyboardActionHandler,androidx.compose.foundation.text.input.TextFieldLineLimits,kotlin.Function2,androidx.compose.foundation.ScrollState,androidx.compose.ui.graphics.Shape,androidx.compose.material3.TextFieldColors,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.interaction.MutableInteractionSource)` (or `https://developer.android.com/reference/kotlin/androidx/compose/material3/OutlinedTextField.composable#OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.text.TextStyle,androidx.compose.material3.TextFieldLabelPosition,kotlin.Function1,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Boolean,androidx.compose.foundation.text.input.InputTransformation,androidx.compose.foundation.text.input.OutputTransformation,androidx.compose.foundation.text.KeyboardOptions,androidx.compose.foundation.text.input.KeyboardActionHandler,androidx.compose.foundation.text.input.TextFieldLineLimits,kotlin.Function2,androidx.compose.foundation.ScrollState,androidx.compose.ui.graphics.Shape,androidx.compose.material3.TextFieldColors,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.interaction.MutableInteractionSource)`) and `https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenu.composable#(androidx.compose.material3.ExposedDropdownMenuBoxScope).ExposedDropdownMenu(kotlin.Boolean,kotlin.Function0,androidx.compose.ui.Modifier,androidx.compose.foundation.ScrollState,kotlin.Boolean,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp,androidx.compose.foundation.BorderStroke,kotlin.Function1)` as content. The `https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenuBoxScope#(androidx.compose.ui.Modifier).menuAnchor(androidx.compose.material3.ExposedDropdownMenuAnchorType,kotlin.Boolean)` modifier should be passed to the text field.

An example of a read-only exposed dropdown menu:

```kotlin
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenu
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuDefaults
import androidx.compose.material3.SelectableDropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier

val options: List<String> = SampleData.take(5)
var expanded by remember { mutableStateOf(false) }
val textFieldState = rememberTextFieldState(options[0])
var checkedIndex by rememberSaveable { mutableIntStateOf(0) }

ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) {
    TextField(
        // The `menuAnchor` modifier must be passed to the text field to handle
        // expanding/collapsing the menu on click. A read-only text field has
        // the anchor type `PrimaryNotEditable`.
        modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
        state = textFieldState,
        readOnly = true,
        lineLimits = TextFieldLineLimits.SingleLine,
        label = { Text("Label") },
        trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
        colors = ExposedDropdownMenuDefaults.textFieldColors(),
    )
    ExposedDropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        containerColor = MenuDefaults.groupStandardContainerColor,
        shape = MenuDefaults.standaloneGroupShape,
    ) {
        val optionCount = options.size
        options.forEachIndexed { index, option ->
            SelectableDropdownMenuItem(
                shapes = MenuDefaults.itemShape(index, optionCount),
                text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
                selected = index == checkedIndex,
                onClick = {
                    textFieldState.setTextAndPlaceCursorAtEnd(option)
                    checkedIndex = index
                    expanded = false
                },
                selectedLeadingIcon = {
                    Icon(
                        Icons.Filled.Check,
                        modifier = Modifier.size(MenuDefaults.LeadingIconSize),
                        contentDescription = null,
                    )
                },
                contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
            )
        }
    }
}
```

An example of an editable exposed dropdown menu:

```kotlin
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenu
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val options: List<String> = SampleData
val textFieldState = rememberTextFieldState()

// The text that the user inputs into the text field can be used to filter the options.
// This sample uses string subsequence matching.
val filteredOptions = options.filteredBy(textFieldState.text)

val (allowExpanded, setExpanded) = remember { mutableStateOf(false) }
val expanded = allowExpanded && filteredOptions.isNotEmpty()

ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = setExpanded) {
    TextField(
        // The `menuAnchor` modifier must be passed to the text field to handle
        // expanding/collapsing the menu on click. An editable text field has
        // the anchor type `PrimaryEditable`.
        modifier =
            Modifier.width(TextFieldDefaults.MinWidth)
                .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
        state = textFieldState,
        lineLimits = TextFieldLineLimits.SingleLine,
        label = { Text("Label") },
        trailingIcon = {
            ExposedDropdownMenuDefaults.TrailingIcon(
                expanded = expanded,
                // If the text field is editable, it is recommended to make the
                // trailing icon a `menuAnchor` of type `SecondaryEditable`. This
                // provides a better experience for certain accessibility services
                // to choose a menu option without typing.
                modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.SecondaryEditable),
            )
        },
        colors = ExposedDropdownMenuDefaults.textFieldColors(),
    )
    ExposedDropdownMenu(
        modifier = Modifier.heightIn(max = 280.dp),
        expanded = expanded,
        onDismissRequest = { setExpanded(false) },
    ) {
        filteredOptions.forEach { option ->
            DropdownMenuItem(
                text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
                onClick = {
                    textFieldState.setTextAndPlaceCursorAtEnd(option.text)
                    setExpanded(false)
                },
                contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
            )
        }
    }
}
```

An example of an editable exposed dropdown menu used like a MultiAutoCompleteTextView:

```kotlin
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.insert
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenu
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.substring
import androidx.compose.ui.unit.dp

/**
 * Returns the TextRange of the current token around the cursor, where commas define token
 * boundaries.
 */
fun TextFieldState.currentTokenRange(): TextRange? {
    if (!selection.collapsed) return null

    val cursor = selection.start
    var start = cursor
    while (start > 0 && text[start - 1] != ',') {
        start--
    }
    while (start < cursor && text[start] == ' ') {
        start++
    }

    var end = cursor
    while (end < text.length && text[end] != ',') {
        end++
    }
    return TextRange(start, end)
}

fun TextFieldState.replaceThenAddComma(start: Int, end: Int, text: CharSequence) = edit {
    replace(start, end, text)
    val afterText = start + text.length
    if (afterText == this.length || this.charAt(afterText) != ',') {
        insert(afterText, ", ")
        placeCursorBeforeCharAt(afterText + 2)
    } else {
        placeCursorAfterCharAt(afterText)
    }
}

val allOptions: List<String> = SampleData
val textFieldState = rememberTextFieldState()
val tokenSelection = textFieldState.currentTokenRange()
val tokenAtCursor =
    if (tokenSelection != null) textFieldState.text.substring(tokenSelection) else ""
val filteredOptions =
    if (tokenAtCursor.isBlank()) emptyList() else allOptions.filteredBy(tokenAtCursor)

val (allowExpanded, setExpanded) = remember { mutableStateOf(false) }
val expanded = allowExpanded && filteredOptions.isNotEmpty()

ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = setExpanded) {
    TextField(
        modifier =
            Modifier.width(TextFieldDefaults.MinWidth)
                .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
        state = textFieldState,
        lineLimits = TextFieldLineLimits.SingleLine,
        label = { Text("Label") },
        trailingIcon = {
            ExposedDropdownMenuDefaults.TrailingIcon(
                expanded = expanded,
                modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.SecondaryEditable),
            )
        },
        colors = ExposedDropdownMenuDefaults.textFieldColors(),
    )
    ExposedDropdownMenu(
        modifier = Modifier.heightIn(max = 280.dp),
        expanded = expanded,
        onDismissRequest = { setExpanded(false) },
    ) {
        filteredOptions.forEach { option ->
            DropdownMenuItem(
                text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
                onClick = {
                    if (tokenSelection != null) {
                        textFieldState.replaceThenAddComma(
                            tokenSelection.start,
                            tokenSelection.end,
                            option,
                        )
                    }
                },
                contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
            )
        }
    }
}
```

| Parameters |
|---|---|
| `expanded: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/index.html` | whether the menu is expanded or not |
| `onExpandedChange: (https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-boolean/index.html) -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | called when the exposed dropdown menu is clicked and the expansion state changes. |
| `modifier: https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier = Modifier` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier` to be applied to this ExposedDropdownMenuBox |
| `content: @https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenuBoxScope.() -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | the content of this ExposedDropdownMenuBox, typically a `https://developer.android.com/reference/kotlin/androidx/compose/material3/TextField.composable#TextField(androidx.compose.foundation.text.input.TextFieldState,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.text.TextStyle,androidx.compose.material3.TextFieldLabelPosition,kotlin.Function1,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Boolean,androidx.compose.foundation.text.input.InputTransformation,androidx.compose.foundation.text.input.OutputTransformation,androidx.compose.foundation.text.KeyboardOptions,androidx.compose.foundation.text.input.KeyboardActionHandler,androidx.compose.foundation.text.input.TextFieldLineLimits,kotlin.Function2,androidx.compose.foundation.ScrollState,androidx.compose.ui.graphics.Shape,androidx.compose.material3.TextFieldColors,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.interaction.MutableInteractionSource)` and an `https://developer.android.com/reference/kotlin/androidx/compose/material3/ExposedDropdownMenu.composable#(androidx.compose.material3.ExposedDropdownMenuBoxScope).ExposedDropdownMenu(kotlin.Boolean,kotlin.Function0,androidx.compose.ui.Modifier,androidx.compose.foundation.ScrollState,kotlin.Boolean,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp,androidx.compose.foundation.BorderStroke,kotlin.Function1)`. |