DropdownMenuPopup
A `Popup` that provides the foundation for building a custom menu.
DropdownMenuPopup
Composable Component
A Popup that provides the foundation for building a custom menu.
Common
@ExperimentalMaterial3ExpressiveApi
@Composable
expect fun DropdownMenuPopup(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = DefaultMenuProperties,
content: @Composable ColumnScope.() -> Unit,
)
Parameters
| expanded | whether the menu is expanded or not. |
| onDismissRequest | called when the user requests to dismiss the menu, such as by tapping outside the menu's bounds. |
| modifier | Modifier to be applied to the menu's content. |
| offset | DpOffset from the original position of the menu. |
| properties | PopupProperties for further customization of this popup's behavior. |
| content | the content of this dropdown menu. |
Android
@ExperimentalMaterial3ExpressiveApi
@Composable
actual fun DropdownMenuPopup(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier,
offset: DpOffset,
properties: PopupProperties,
content: @Composable ColumnScope.() -> Unit,
)
Code Examples
GroupedMenuSample
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun GroupedMenuSample() {
val groupInteractionSource = remember { MutableInteractionSource() }
var expanded by remember { mutableStateOf(false) }
var homeChecked by remember { mutableStateOf(false) }
val groupLabels = listOf("Modification", "Navigation")
val groupItemLabels = listOf(listOf("Edit", "Settings"), listOf("Home", "More Options"))
val groupItemLeadingIcons =
listOf(
listOf(Icons.Outlined.Edit, Icons.Outlined.Settings),
listOf(null, Icons.Outlined.Info),
)
val groupItemCheckedLeadingIcons =
listOf(
listOf(Icons.Filled.Edit, Icons.Filled.Settings),
listOf(Icons.Filled.Check, Icons.Filled.Info),
)
val groupItemTrailingIcons: List<List<ImageVector?>> =
listOf(listOf(null, null), listOf(Icons.Outlined.Home, Icons.Outlined.MoreVert))
val groupItemCheckedTrailingIcons: List<List<ImageVector?>> =
listOf(listOf(null, null), listOf(Icons.Filled.Home, Icons.Filled.MoreVert))
val groupItemSupportingText: List<List<String?>> =
listOf(listOf("Edit mode", null), listOf(null, "Opens menu"))
val checked = remember {
listOf(mutableStateListOf(false, false), mutableStateListOf(false, false))
}
Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
// Icon button should have a tooltip associated with it for a11y.
TooltipBox(
positionProvider =
TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { PlainTooltip { Text("Localized description") } },
state = rememberTooltipState(),
) {
IconButton(onClick = { expanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
}
}
DropdownMenuPopup(expanded = expanded, onDismissRequest = { expanded = false }) {
val groupCount = groupLabels.size
groupLabels.fastForEachIndexed { groupIndex, label ->
DropdownMenuGroup(
shapes = MenuDefaults.groupShape(groupIndex, groupCount),
interactionSource = groupInteractionSource,
) {
MenuDefaults.Label { Text(label) }
HorizontalDivider(
modifier = Modifier.padding(MenuDefaults.HorizontalDividerPadding)
)
val groupItemCount = groupItemLabels[groupIndex].size
groupItemLabels[groupIndex].fastForEachIndexed { itemIndex, itemLabel ->
DropdownMenuItem(
text = { Text(itemLabel) },
supportingText =
groupItemSupportingText[groupIndex][itemIndex]?.let { supportingText
->
{ Text(supportingText) }
},
shapes = MenuDefaults.itemShape(itemIndex, groupItemCount),
leadingIcon =
groupItemLeadingIcons[groupIndex][itemIndex]?.let { iconData ->
{
Icon(
iconData,
modifier = Modifier.size(MenuDefaults.LeadingIconSize),
contentDescription = null,
)
}
},
checkedLeadingIcon = {
Icon(
groupItemCheckedLeadingIcons[groupIndex][itemIndex],
modifier = Modifier.size(MenuDefaults.LeadingIconSize),
contentDescription = null,
)
},
trailingIcon =
if (checked[groupIndex][itemIndex]) {
groupItemCheckedTrailingIcons[groupIndex][itemIndex]?.let {
iconData ->
{
Icon(
iconData,
modifier =
Modifier.size(MenuDefaults.TrailingIconSize),
contentDescription = null,
)
}
}
} else {
groupItemTrailingIcons[groupIndex][itemIndex]?.let { iconData ->
{
Icon(
iconData,
modifier =
Modifier.size(MenuDefaults.TrailingIconSize),
contentDescription = null,
)
}
}
},
checked = checked[groupIndex][itemIndex],
onCheckedChange = { checked[groupIndex][itemIndex] = it },
)
}
}
if (groupIndex != groupCount - 1) {
Spacer(Modifier.height(MenuDefaults.GroupSpacing))
}
}
if (checked.last().last()) {
DropdownMenuButtonGroup()
}
}
}
}