WindowDropdownMenu
WindowDropdownMenu is a BasicComponent wrapper that opens a WindowDropdownPopup (rendered at window level via Dialog) when clicked. Unlike WindowDropdownPreference, it does not own a single selection index — selection state lives entirely on each DropdownItem's selected and onClick. Use it for action menus, multi-select menus, or any case where the items in a popup do not share one mutually exclusive choice.
Import
import io.github.suqi8.coui.kmp.menu.WindowDropdownMenu
import io.github.suqi8.coui.kmp.basic.DropdownEntry
import io.github.suqi8.coui.kmp.basic.DropdownItemBasic Usage
Wrap a single DropdownEntry to render a basic dropdown menu row. No Scaffold is required:
var selectedIndex by remember { mutableStateOf(0) }
val entry = DropdownEntry(
items = listOf("Option 1", "Option 2", "Option 3").mapIndexed { index, text ->
DropdownItem(
text = text,
selected = selectedIndex == index,
onClick = { selectedIndex = index },
)
}
)
WindowDropdownMenu(
title = "Dropdown Menu",
entry = entry
)Grouped Menu
Pass a List<DropdownEntry> to render multiple groups separated by dividers. By default, collapseOnSelection is entries.size <= 1, so multi-group menus stay open after each selection.
var sizeIndex by remember { mutableStateOf(0) }
var colorIndex by remember { mutableStateOf(0) }
val entries = listOf(
DropdownEntry(
items = listOf("Small", "Medium").mapIndexed { index, text ->
DropdownItem(text = text, selected = sizeIndex == index, onClick = { sizeIndex = index })
}
),
DropdownEntry(
items = listOf("Red", "Green", "Blue").mapIndexed { index, text ->
DropdownItem(text = text, selected = colorIndex == index, onClick = { colorIndex = index })
}
)
)
WindowDropdownMenu(
title = "Grouped Menu",
entries = entries,
collapseOnSelection = false
)Multi Select
Selection state lives on DropdownItem.selected, so multiple items can be selected simultaneously by toggling each item's value from onClick.
var selected by remember { mutableStateOf(setOf("A1", "B2")) }
val entries = listOf(
DropdownEntry(
items = listOf("A1", "A2").map { text ->
DropdownItem(
text = text,
selected = text in selected,
onClick = {
selected = if (text in selected) selected - text else selected + text
}
)
}
),
DropdownEntry(
items = listOf("B1", "B2", "B3").map { text ->
DropdownItem(
text = text,
selected = text in selected,
onClick = {
selected = if (text in selected) selected - text else selected + text
}
)
}
)
)
WindowDropdownMenu(
title = "Multi Select Menu",
entries = entries,
collapseOnSelection = false
)Observe Expanded State
var expanded by remember { mutableStateOf(false) }
val entry = DropdownEntry(
items = listOf("Option 1", "Option 2", "Option 3").map { DropdownItem(text = it) }
)
WindowDropdownMenu(
title = "Observe Expanded",
summary = if (expanded) "Expanded" else "Collapsed",
entry = entry,
onExpandedChange = { expanded = it }
)Items with a Hint Slot
The hint slot renders between the title block and the selection indicator, capped at 40dp wide. It suits a red dot, a count badge, or a very short label. Matching ColorOS, the hint is hidden entirely while the row is disabled.
val entry = DropdownEntry(
items = listOf(
DropdownItem(text = "Inbox", hint = { Badge(count = 12) }),
DropdownItem(text = "Updates", hint = { Badge() }),
// The badge is suppressed because the row is disabled.
DropdownItem(text = "Archive", hint = { Badge(count = 3) }, enabled = false),
)
)
Scaffold {
WindowDropdownMenu(title = "Hints", entry = entry)
}Group Headers
A DropdownEntry can declare a title, rendered above its items as a non-clickable header row (12sp medium, secondary label color, at most 2 lines). Headers coexist with the group divider that already separates adjacent entries.
val entries = listOf(
DropdownEntry(
title = "Sort by",
items = listOf("Name", "Date modified").map { DropdownItem(text = it) }
),
DropdownEntry(
title = "Order",
items = listOf("Ascending", "Descending").map { DropdownItem(text = it) }
)
)
Scaffold {
WindowDropdownMenu(title = "Group Headers", entries = entries)
}Alert Items
Set alert = true to mark a destructive action. Its title uses the error color instead of the normal label color; disabled alert rows still fall back to the disabled color.
val entry = DropdownEntry(
items = listOf(
DropdownItem(text = "Rename"),
DropdownItem(text = "Delete", alert = true),
)
)
Scaffold {
WindowDropdownMenu(title = "Alert Item", entry = entry)
}Component States
Disabled State
WindowDropdownMenu(
title = "Disabled Menu",
summary = "This menu is currently unavailable",
entry = DropdownEntry(items = listOf(DropdownItem(text = "Option 1"))),
enabled = false
)The menu is also implicitly disabled when no DropdownEntry contains any items.
Properties
WindowDropdownMenu Properties (Entries Overload)
| Property Name | Type | Description | Default Value | Required |
|---|---|---|---|---|
| entries | List<DropdownEntry> | Dropdown entry groups separated by dividers | - | Yes |
| title | String | Title of the menu row | - | Yes |
| modifier | Modifier | Modifier applied to the component | Modifier | No |
| titleColor | BasicComponentColors | Title text color configuration | BasicComponentDefaults.titleColor() | No |
| summary | String? | Summary description of the menu | null | No |
| summaryColor | BasicComponentColors | Summary text color configuration | BasicComponentDefaults.summaryColor() | No |
| dropdownColors | DropdownColors | Color configuration for dropdown items | DropdownDefaults.dropdownColors() | No |
| startAction | @Composable (() -> Unit)? | Custom start side content | null | No |
| bottomAction | (@Composable () -> Unit)? | Custom bottom side content | null | No |
| insideMargin | PaddingValues | Internal content padding | BasicComponentDefaults.InsideMargin | No |
| maxHeight | Dp? | Maximum height of the dropdown popup | null | No |
| enabled | Boolean | Whether component is interactive | true | No |
| collapseOnSelection | Boolean | Whether to close the popup after each selection | entries.size <= 1 | No |
| onExpandedChange | ((Boolean) -> Unit)? | Callback when the expanded state changes | null | No |
Entry Overload Properties
| Property Name | Type | Description | Default Value | Required |
|---|---|---|---|---|
| entry | DropdownEntry | Single dropdown entry group | - | Yes |
| collapseOnSelection | Boolean | Whether to close the popup after selection | true | No |
All other parameters are identical to the entries overload above.
DropdownEntry Properties
| Property Name | Type | Description | Default Value | Required |
|---|---|---|---|---|
| items | List<DropdownItem> | Items shown in this dropdown group | - | Yes |
| enabled | Boolean | Whether this group is enabled. False disables all items; true still respects each item's enabled state | true | No |
| title | String? | Optional non-clickable group header rendered above the items (12sp medium, secondary label, max 2 lines) | null | No |
DropdownItem Properties
| Property Name | Type | Description | Default Value | Required |
|---|---|---|---|---|
| text | String | Text shown for the item | - | Yes |
| enabled | Boolean | Whether the item can be clicked. Disabled items are gray | true | No |
| selected | Boolean | Whether the item is selected | false | No |
| onClick | (() -> Unit)? | Callback invoked when the item is clicked | null | No |
| icon | @Composable ((Modifier) -> Unit)? | Icon shown before the item text | null | No |
| summary | String? | Summary text shown below the item text | null | No |
| children | List<DropdownItem>? | Optional submenu items; cascading variants only | null | No |
| hint | @Composable (() -> Unit)? | Optional trailing hint slot (badge, red dot, short count) shown before the selection indicator, width-capped at 40dp. Hidden entirely while the row is disabled | null | No |
| alert | Boolean | Whether this is an alert (destructive) item; its title uses the error color | false | No |
DropdownColors Properties
| Property Name | Type | Description |
|---|---|---|
| contentColor | Color | Color of the option title |
| summaryColor | Color | Color of the option summary |
| containerColor | Color | Background color of the option |
| selectedContentColor | Color | Title color of the selected option |
| selectedSummaryColor | Color | Summary color of the selected option |
| selectedContainerColor | Color | Background color of the selected option |
| selectedIndicatorColor | Color | Color of the selected indicator icon |
| disabledContentColor | Color | Title color of a disabled option |
| alertContentColor | Color | Title color of an alert option |
| headerColor | Color | Title color of a group header row |

