ARTICLE AD BOX
Yeah this is a known issue with DropdownMenu inside Dialog. The problem is Dialog creates its own window, and DropdownMenu uses Popup internally which calculates position relative to that window's origin (0,0) instead of your actual anchor. So your Box wrapping doesn't help because the coordinates get lost.
I dealt with this same thing and found two approaches that work:
Option 1: Use AlertDialog instead
AlertDialog handles the window differently and dropdown anchoring just works inside it.
@Composable fun CustomComposeDialog(onDismiss: () -> Unit) { var textInput1 by remember { mutableStateOf("") } var textInput2 by remember { mutableStateOf("") } var headerMenuExpanded by remember { mutableStateOf(false) } val textFieldMenuExpanded = textInput1.length > 3 AlertDialog( onDismissRequest = onDismiss, confirmButton = { TextButton(onClick = onDismiss) { Text("Close") } }, title = { Text( text = "Custom Entry", style = MaterialTheme.typography.headlineSmall ) }, text = { Column( verticalArrangement = Arrangement.spacedBy(16.dp) ) { Box { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text("Category Options") IconButton(onClick = { headerMenuExpanded = true }) { Icon(Icons.Default.ArrowDropDown, contentDescription = null) } } DropdownMenu( expanded = headerMenuExpanded, onDismissRequest = { headerMenuExpanded = false } ) { DropdownMenuItem( text = { Text("Option A") }, onClick = { headerMenuExpanded = false } ) DropdownMenuItem( text = { Text("Option B") }, onClick = { headerMenuExpanded = false } ) } } Box { OutlinedTextField( value = textInput1, onValueChange = { textInput1 = it }, label = { Text("Search or Type...") }, modifier = Modifier.fillMaxWidth() ) DropdownMenu( expanded = textFieldMenuExpanded, onDismissRequest = { }, properties = PopupProperties(focusable = false) ) { listOf("Result 1", "Result 2", "Result 3").forEach { suggestion -> DropdownMenuItem( text = { Text(suggestion) }, onClick = { textInput1 = suggestion } ) } } } OutlinedTextField( value = textInput2, onValueChange = { textInput2 = it }, label = { Text("Additional Notes") }, modifier = Modifier.fillMaxWidth() ) } } ) }This works but you lose some layout control since AlertDialog has its own structure.
Option 2: Keep Dialog but use ExposedDropdownMenuBox
This is what I actually ended up using. ExposedDropdownMenuBox handles its own anchoring internally so it works fine inside Dialog.
@OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomComposeDialog(onDismiss: () -> Unit) { var textInput1 by remember { mutableStateOf("") } var textInput2 by remember { mutableStateOf("") } var headerMenuExpanded by remember { mutableStateOf(false) } var textFieldMenuExpanded by remember { mutableStateOf(false) } Dialog(onDismissRequest = onDismiss) { Surface( shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surface, modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Column( modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text( text = "Custom Entry", style = MaterialTheme.typography.headlineSmall ) // Header dropdown ExposedDropdownMenuBox( expanded = headerMenuExpanded, onExpandedChange = { headerMenuExpanded = it } ) { Row( modifier = Modifier .fillMaxWidth() .menuAnchor(MenuAnchorType.PrimaryNotEditable) .clickable { headerMenuExpanded = true }, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text("Category Options") Icon(Icons.Default.ArrowDropDown, contentDescription = null) } ExposedDropdownMenu( expanded = headerMenuExpanded, onDismissRequest = { headerMenuExpanded = false } ) { DropdownMenuItem( text = { Text("Option A") }, onClick = { headerMenuExpanded = false } ) DropdownMenuItem( text = { Text("Option B") }, onClick = { headerMenuExpanded = false } ) } } // TextField with suggestions ExposedDropdownMenuBox( expanded = textFieldMenuExpanded, onExpandedChange = { textFieldMenuExpanded = it } ) { OutlinedTextField( value = textInput1, onValueChange = { textInput1 = it textFieldMenuExpanded = it.length > 3 }, label = { Text("Search or Type...") }, modifier = Modifier .fillMaxWidth() .menuAnchor(MenuAnchorType.PrimaryEditable) ) if (textInput1.length > 3) { ExposedDropdownMenu( expanded = textFieldMenuExpanded, onDismissRequest = { textFieldMenuExpanded = false } ) { listOf("Result 1", "Result 2", "Result 3").forEach { suggestion -> DropdownMenuItem( text = { Text(suggestion) }, onClick = { textInput1 = suggestion textFieldMenuExpanded = false } ) } } } } OutlinedTextField( value = textInput2, onValueChange = { textInput2 = it }, label = { Text("Additional Notes") }, modifier = Modifier.fillMaxWidth() ) TextButton( onClick = onDismiss, modifier = Modifier.align(Alignment.End) ) { Text("Close") } } } } }The important bits:
ExposedDropdownMenuBox manages anchoring with the menuAnchor() modifier so it works even inside a Dialog window
Use MenuAnchorType.PrimaryNotEditable for the header row since it's not a text field
Use MenuAnchorType.PrimaryEditable for the text field so user can keep typing while menu is open
Control expanded state in onValueChange based on your length > 3 logic
