Tom Truyen
11/11/2023, 4:25 PM@Composable
fun TextFieldWithDropdown(
modifier: Modifier = Modifier,
value: String,
onValueChange: (String) -> Unit,
options: List<String>,
onOptionClick: (String) -> Unit,
) {
val density = LocalDensity.current
var text by remember {
mutableStateOf(value)
}
var expanded by remember(options) { mutableStateOf(options.isNotEmpty()) }
var anchorSize by remember {
mutableStateOf(DpSize.Zero)
}
Box(modifier) {
TextField(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned {
anchorSize = with(density) {
DpSize(
width = it.size.width.toDp(),
height = it.size.height.toDp()
)
}
}
.onFocusChanged { focusState ->
if (!focusState.isFocused) {
expanded = false
}
},
singleLine = true,
value = text,
onValueChange = {
text = it
onValueChange(it)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = MaterialTheme.colors.surface,
textColor = MaterialTheme.colors.onSurface,
cursorColor = MaterialTheme.colors.onSurface,
),
)
DropdownMenu(
modifier = Modifier
.width(anchorSize.width),
offset = DpOffset(0.dp, Dimens.normal),
focusable = false,
expanded = expanded,
onDismissRequest = { expanded = false }
) {
options.forEach { text ->
DropdownMenuItem(
onClick = {
onOptionClick(text)
}
) {
Text(text = text)
}
}
}
}
}
Alexander Maryanovsky
11/11/2023, 4:38 PMExposedDropdownMenuBox
and see how it does that.val windowInfo = LocalWindowInfo.current
Box(
modifier.onGloballyPositioned {
width = it.size.width
val boundsInWindow = it.boundsInWindow()
val visibleWindowBounds = windowInfo.containerSize.toIntRect()
val heightAbove = boundsInWindow.top - visibleWindowBounds.top
val heightBelow = visibleWindowBounds.height - boundsInWindow.bottom
menuHeight = max(heightAbove, heightBelow).toInt() - verticalMarginInPx
}
)
Tom Truyen
11/11/2023, 4:42 PMAlexander Maryanovsky
11/11/2023, 4:42 PMDropdownMenu
will refuse to position too close to the top/bottom of the window, which will look weird if your textfield is smaller than the standard material one.max(heightAbove, heightBelow).toInt() - verticalMarginInPx
but the popup positioning requires that it doesn’t extend into the vertical margin both at the top and the bottom.Tom Truyen
11/11/2023, 5:24 PMAlexander Maryanovsky
11/11/2023, 5:24 PMval visibleWindowBounds = IntRect(
left = 0,
top = verticalMarginInPx,
right = windowInfo.containerSize.width,
bottom = windowInfo.containerSize.height - 2*verticalMarginInPx
)
menuHeight
above, convert it to dp
and pass it to Modifier.heightIn(max = menuHeightDp)
on the DropdownMenu
Tom Truyen
11/11/2023, 5:34 PMboundsInWindow
extension function? Looks quite interestingAlexander Maryanovsky
11/11/2023, 5:36 PMTom Truyen
11/11/2023, 5:36 PMAlexander Maryanovsky
11/11/2023, 5:49 PMExposedDropdownMenuBox
?Tom Truyen
11/11/2023, 5:50 PMAlexander Maryanovsky
11/11/2023, 5:50 PMTom Truyen
11/11/2023, 5:53 PM