1

How to create a dropdown menu items on a button click. In Jetpack compose?

Like here but for buttons :

      DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false },
            toggle = iconButton,
            dropdownOffset = Position(24.dp, 0.dp),
            toggleModifier = modifier
        ) {
            options.forEach {
                DropdownMenuItem(onClick = {}) {
                    Text(it)
                }
            }
        }
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Nurseyit Tursunkulov
  • 8,012
  • 12
  • 44
  • 78
  • 1
    No idea what you want to achieve, but the parameter toggle can be used for anything you want. So if you want a regular button and not an icon button just replace it with ``toggle = {Button(onClick={setExpanded(!expanded)}){Text("Open Dropdown")}}``. I hope this helped you. – 2jan222 Dec 30 '20 at 21:08

3 Answers3

3

The previous answer is correct, but the key part is missing. Both, DropdownMenu and the button that opens it suppose to be wrapped in Box. Only this way the opening button will be used as an anchor for the menu. This is my version:

@Composable
fun DropdownMenu(
    colorSelected: Color = scColors.primary,
    colorBackground: Color = scColors.onSurface,
    expanded: Boolean,
    selectedIndex: Int,
    items: List<String>,
    onSelect: (Int) -> Unit,
    onDismissRequest: () -> Unit,
    content: @Composable () -> Unit
) {
    Box {
        content()
        DropdownMenu(
            expanded = expanded,
            onDismissRequest = onDismissRequest,
            modifier = Modifier
                .height(300.dp)
                .fillMaxWidth()
                .background(
                    color = colorBackground,
                    shape = RoundedCornerShape(16.dp)
                )
        ) {
            items.forEachIndexed { index, s ->
                if (selectedIndex == index) {
                    DropdownMenuItem(
                        modifier = Modifier
                            .fillMaxWidth()
                            .background(
                                color = colorSelected,
                                shape = RoundedCornerShape(16.dp)
                            ),
                        onClick = { onSelect(index) }
                    ) {
                        Text(
                            text = s,
                            color = Color.Black,
                            textAlign = TextAlign.Center,
                            modifier = Modifier.fillMaxWidth()
                        )
                    }
                } else {
                    DropdownMenuItem(
                        modifier = Modifier.fillMaxWidth(),
                        onClick = { onSelect(index) }
                    ) {
                        Text(
                            text = s,
                            color = Color.DarkGray,
                            textAlign = TextAlign.Center,
                            modifier = Modifier.fillMaxWidth()
                        )
                    }
                }
            }
        }
    }
}

And, then a DropdownMenu accepts the opening anchor button as a content:

val items = listOf(
    "English",
    "Russian",
    "Spanish",
    "French",
    "German",
    "Hebrew"
)

@Preview
@Composable
fun TestDropdownMenu() {
    var expanded by remember { mutableStateOf(false) }

    var selectedIndex by remember { mutableStateOf(0) }
    val buttonTitle = items[selectedIndex]
    DropdownMenu(
        colorSelected = scColors.onSurface,
        colorBackground = scColors.primary,
        expanded = expanded,
        selectedIndex = selectedIndex,
        items = items,
        onSelect = { index ->
            selectedIndex = index
            expanded = false
        },
        onDismissRequest = {
            expanded = false
        }) {

        Button(
            onClick = {
                expanded = true
            }
        ) {
            Text(
                text = buttonTitle,
                color = Color.Black,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}
Sergey Neskoromny
  • 1,188
  • 8
  • 15
1

You can use something like:

   var expanded by remember { mutableStateOf(false) }

   Button(onClick = { expanded = true }){
        Text ("...")
    }
    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        //....
    ) {
        items.forEachIndexed { index, s ->
            //....
        }
    }
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
0

you can create a dropdown list in compose by using this

list : list you want to show

label : label is the hint to show in the textview

default : to set default value in textview

validateInput = you can validate the input by changing the validateInput state to true on the button clicked and handle it accordingly

fun dropdownList(
list: List<String>,
label: String,
defaultValue: String = "",
validateInput: Boolean
): String {

var expanded by remember { mutableStateOf(false) }
var selectedText by remember { mutableStateOf(defaultValue) }
var textFieldSize by remember { mutableStateOf(Size.Zero) }
var isError by remember { mutableStateOf(false) }

if (validateInput && selectedText.isEmpty())
    isError = true

val icon = if (expanded)
    Icons.Filled.ArrowDropUp
else
    Icons.Filled.ArrowDropDown


Column(modifier = Modifier.padding(bottom = 2.dp, top = 2.dp)) {
    OutlinedTextField(
        value = selectedText,
        onValueChange = {
            selectedText = it
        },
        modifier = Modifier
            .fillMaxWidth()
            .onGloballyPositioned { coordinates ->
                textFieldSize = coordinates.size.toSize()
            },
        label = { Text(label) },
        trailingIcon = {
            Icon(icon, "contentDescription",
                Modifier.clickable { expanded = !expanded })
        },
        isError = isError
    )
    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        modifier = Modifier
            .width(with(LocalDensity.current) { textFieldSize.width.toDp() })
    ) {
        list.forEach { label ->
            DropdownMenuItem(onClick = {
                selectedText = label
                expanded = false
            }) {
                Text(text = label)
            }
        }
    }
    if (isError) {
        Text(
            text = "$label can't be empty",
            color = Color.Red,
            textAlign = TextAlign.End,
            modifier = Modifier.fillMaxWidth()
        )
    }
}
return selectedText
}

Github gist link DropdownList.kt

Hitesh Patel
  • 183
  • 2
  • 12