Skip to content

OverlaySpinnerPreference

OverlaySpinnerPreference is a dropdown selector component in Miuix that provides titles, summaries, and a list of options with icons and text. It supports click interaction and various display modes, commonly used in option settings with visual aids. This component is similar to OverlayDropdownPreference but offers richer functionality and interaction experience.

Prerequisite

This component depends on Scaffold providing MiuixPopupHost to render popup content. It must be used within Scaffold, otherwise popup content will not render correctly.

Import

kotlin
import top.yukonga.miuix.kmp.preference.OverlaySpinnerPreference
import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.DropdownItem

Basic Usage

The OverlaySpinnerPreference component provides basic dropdown selector functionality:

kotlin
var selectedIndex by remember { mutableStateOf(0) }
val options = listOf(
    DropdownItem(text = "Option 1"),
    DropdownItem(text = "Option 2"),
    DropdownItem(text = "Option 3"),
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Dropdown Selector",
        items = options,
        selectedIndex = selectedIndex,
        onSelectedIndexChange = { selectedIndex = it }
    )
}

Options with Icons and Summaries

kotlin
// Create a rounded rectangle Painter
class RoundedRectanglePainter(
    private val cornerRadius: Dp = 6.dp
) : Painter() {
    override val intrinsicSize = Size.Unspecified

    override fun DrawScope.onDraw() {
        drawRoundRect(
            color = Color.White,
            size = Size(size.width, size.height),
            cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx())
        )
    }
}

var selectedIndex by remember { mutableStateOf(0) }
val options = listOf(
    DropdownItem(
        icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) },
        text = "Red Theme",
        summary = "Vibrant red"
    ),
    DropdownItem(
        icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) },
        text = "Blue Theme",
        summary = "Calm blue"
    ),
    DropdownItem(
        icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) },
        text = "Green Theme",
        summary = "Fresh green"
    ),
    DropdownItem(
        icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) },
        text = "Yellow Theme",
        summary = "Bright yellow"
    )
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Function Selection",
        summary = "Choose the action you want to perform",
        items = options,
        selectedIndex = selectedIndex,
        onSelectedIndexChange = { selectedIndex = it }
    )
}

Component States

Disabled State

kotlin
OverlaySpinnerPreference(
    title = "Disabled Selector",
    summary = "This selector is currently unavailable",
    items = listOf(DropdownItem(text = "Option 1")),
    selectedIndex = 0,
    onSelectedIndexChange = {},
    enabled = false
)

Dialog Mode

OverlaySpinnerPreference also supports dialog mode, suitable for displaying a larger number of options or when a more prominent selection interface is needed. This mode can be activated by providing the dialogButtonString parameter.

kotlin
var selectedIndex by remember { mutableStateOf(0) }
val options = listOf(
    DropdownItem(text = "Option 1"),
    DropdownItem(text = "Option 2"),
    DropdownItem(text = "Option 3")
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Dialog Mode",
        dialogButtonString = "Cancel",
        items = options,
        selectedIndex = selectedIndex,
        onSelectedIndexChange = { selectedIndex = it }
    )
}

Grouped Options

Use DropdownEntry when you need a custom item list with selected states, disabled items, item callbacks, icons, or summaries. Use entries to show multiple option groups separated by dividers.

kotlin
var firstSelectedIndex by remember { mutableStateOf(0) }
var secondSelectedIndex by remember { mutableStateOf(0) }
val entries = listOf(
    DropdownEntry(
        items = listOf("Small", "Medium").mapIndexed { index, text ->
            DropdownItem(text = text, selected = firstSelectedIndex == index, onClick = { firstSelectedIndex = index })
        }
    ),
    DropdownEntry(
        items = listOf("Red", "Green", "Blue").mapIndexed { index, text ->
            DropdownItem(text = text, selected = secondSelectedIndex == index, onClick = { secondSelectedIndex = index })
        }
    )
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Grouped Selector",
        entries = entries,
        collapseOnSelection = false
    )
}

For the entries overload, collapseOnSelection controls whether the popup closes after an item is selected. It defaults to entries.size <= 1, so a single group closes after selection while multiple groups stay open for consecutive changes. The same entry and entries overloads are also available in dialog mode by providing dialogButtonString.

Multi Select

Because selection state lives on DropdownItem, multiple items can be selected by keeping a set of selected values and toggling each item from onClick.

kotlin
var selectedItems by remember { mutableStateOf(setOf("A1", "B2")) }
val entries = listOf(
    DropdownEntry(
        items = listOf("A1", "A2").map { text ->
            DropdownItem(
                text = text,
                selected = text in selectedItems,
                onClick = {
                    selectedItems = if (text in selectedItems) selectedItems - text else selectedItems + text
                }
            )
        }
    ),
    DropdownEntry(
        items = listOf("B1", "B2", "B3").map { text ->
            DropdownItem(
                text = text,
                selected = text in selectedItems,
                onClick = {
                    selectedItems = if (text in selectedItems) selectedItems - text else selectedItems + text
                }
            )
        }
    )
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Multi Select Selector",
        entries = entries,
        collapseOnSelection = false
    )
}

Observe Expanded State

kotlin
var selectedIndex by remember { mutableStateOf(0) }
var expanded by remember { mutableStateOf(false) }
val options = listOf(
    DropdownItem(text = "Option 1"),
    DropdownItem(text = "Option 2"),
    DropdownItem(text = "Option 3"),
)

Scaffold {
    OverlaySpinnerPreference(
        title = "Dropdown Selector",
        summary = if (expanded) "Expanded" else "Collapsed",
        items = options,
        selectedIndex = selectedIndex,
        onExpandedChange = { expanded = it },
        onSelectedIndexChange = { selectedIndex = it }
    )
}

Properties

OverlaySpinnerPreference Properties (Dropdown Mode)

Property NameTypeDescriptionDefault ValueRequired
itemsList<DropdownItem>Options list-Yes
selectedIndexIntCurrent selected item index-Yes
titleStringSelector title-Yes
modifierModifierComponent modifierModifierNo
titleColorBasicComponentColorsTitle text color configBasicComponentDefaults.titleColor()No
summaryString?Selector descriptionnullNo
summaryColorBasicComponentColorsSummary text color configBasicComponentDefaults.summaryColor()No
spinnerColorsDropdownColorsColor configuration for spinnerDropdownDefaults.dropdownColors()No
startAction@Composable (() -> Unit)?Custom start side contentnullNo
bottomAction@Composable (() -> Unit)?Custom bottom side contentnullNo
insideMarginPaddingValuesInternal content paddingBasicComponentDefaults.InsideMarginNo
maxHeightDp?Maximum dropdown heightnullNo
enabledBooleanInteractive statetrueNo
showValueBooleanShow current selected valuetrueNo
renderInRootScaffoldBooleanWhether to render the popup in the root (outermost) Scaffold. When true, the popup covers the full screen. When false, it renders within the current Scaffold's bounds with position compensationtrueNo
onExpandedChange((Boolean) -> Unit)?Callback when expanded state changesnullNo
onSelectedIndexChange((Int) -> Unit)?Selection change callback-No

Entry Overload Properties

Property NameTypeDescriptionDefault ValueRequired
entryDropdownEntrySingle dropdown entry group-Yes
collapseOnSelectionBooleanWhether to close the popup after selectiontrueNo

Grouped Entries Overload Properties

Property NameTypeDescriptionDefault ValueRequired
entriesList<DropdownEntry>Dropdown entry groups separated by dividers-Yes
collapseOnSelectionBooleanWhether to close the popup after each selectionentries.size <= 1No
renderInRootScaffoldBooleanWhether to render the popup in the root ScaffoldtrueNo

OverlaySpinnerPreference Properties (Dialog Mode)

Property NameTypeDescriptionDefault ValueRequired
itemsList<DropdownItem>Options list-Yes
selectedIndexIntCurrent selected item index-Yes
titleStringSelector title-Yes
dialogButtonStringStringDialog bottom button text-Yes
modifierModifierComponent modifierModifierNo
popupModifierModifierDialog popup modifierModifierNo
titleColorBasicComponentColorsTitle text color configBasicComponentDefaults.titleColor()No
summaryString?Selector descriptionnullNo
summaryColorBasicComponentColorsSummary text color configBasicComponentDefaults.summaryColor()No
spinnerColorsDropdownColorsColor configuration for spinnerDropdownDefaults.dialogDropdownColors()No
startAction@Composable (() -> Unit)?Custom start side contentnullNo
bottomAction@Composable (() -> Unit)?Custom bottom side contentnullNo
insideMarginPaddingValuesInternal content paddingBasicComponentDefaults.InsideMarginNo
enabledBooleanInteractive statetrueNo
showValueBooleanShow current selected valuetrueNo
renderInRootScaffoldBooleanWhether to render the dialog in the root (outermost) Scaffold. When true, the dialog covers the full screen. When false, it renders within the current Scaffold's boundstrueNo
onExpandedChange((Boolean) -> Unit)?Callback when expanded state changesnullNo
onSelectedIndexChange((Int) -> Unit)?Selection change callback-No

Dialog Entry Overload Properties

Property NameTypeDescriptionDefault ValueRequired
entryDropdownEntrySingle dropdown entry group-Yes
dialogButtonStringStringDialog bottom button text-Yes
collapseOnSelectionBooleanWhether to close the dialog after selectiontrueNo

Dialog Grouped Entries Overload Properties

Property NameTypeDescriptionDefault ValueRequired
entriesList<DropdownEntry>Dropdown entry groups separated by dividers-Yes
dialogButtonStringStringDialog bottom button text-Yes
collapseOnSelectionBooleanWhether to close the dialog after each selectionentries.size <= 1No
renderInRootScaffoldBooleanWhether to render the dialog in the root ScaffoldtrueNo
Property NameTypeDescriptionDefault ValueRequired
itemsList<DropdownItem>Items shown in this dropdown group-Yes
enabledBooleanWhether this group is enabled. False disables all items; true still respects each item's enabled statetrueNo

Group titles are reserved for future use. The original MIUI dropdown style currently has no matching group-title presentation, so the title field is not exposed yet.

Property NameTypeDescriptionDefault ValueRequired
textStringText shown for the item-Yes
enabledBooleanWhether the item can be clicked. Disabled items are graytrueNo
selectedBooleanWhether the item is selectedfalseNo
onClick(() -> Unit)?Callback invoked when the item is clickednullNo
icon@Composable ((Modifier) -> Unit)?Icon shown before the item textnullNo
summaryString?Summary text shown below the item textnullNo
childrenList<DropdownItem>?Optional submenu items; cascading variants onlynullNo
Property NameTypeDescription
contentColorColorColor of the option title
summaryColorColorColor of the option summary
containerColorColorBackground color of the option
selectedContentColorColorTitle color of the selected option
selectedSummaryColorColorSummary color of the selected option
selectedContainerColorColorBackground color of the selected option
selectedIndicatorColorColorColor of the selected indicator icon

Changelog

Released under the Apache-2.0 License