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