Nurseyit Tursunkulov
Nurseyit Tursunkulov

Reputation: 9380

dropdown list on button click compose

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)
                }
            }
        }

Upvotes: 1

Views: 5701

Answers (3)

Hitesh Patel
Hitesh Patel

Reputation: 301

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

Upvotes: 0

Sergey Neskoromny
Sergey Neskoromny

Reputation: 1208

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
            )
        }
    }
}

Upvotes: 4

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363935

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 ->
            //....
        }
    }

Upvotes: 1

Related Questions