Column垂直滚动时ExposedDropdownMenuBox及关联TextField频繁重组问题
现象
给Column加上verticalScroll修饰符后,上下滚动页面时,绑定了.menuAnchor()的ExposedDropdownMenuBox和它关联的TextField会反复重组。不管是最简复现示例还是实际项目里,这个问题都存在。
最简复现代码
Column( modifier = modifier .fillMaxWidth() .verticalScroll( rememberScrollState(), flingBehavior = null ) ) { val options by remember { mutableStateOf(listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")) } var expanded by remember { mutableStateOf(false) } var name by remember { mutableStateOf("") } Row() { ExposedDropdownMenuBox( modifier = modifier, expanded = expanded, onExpandedChange = { expanded = !expanded } ) { TextField( modifier = modifier.menuAnchor(), value = "name", onValueChange = { }, ) } } Row() {...} Row() {...} Row() {...} // 更多行元素... }
已尝试的无效操作
- 把每行拆成独立的Composable
- 把状态变量移到不同类里管理
- 修改状态变量的类型
- 简化
ExposedDropdownMenuBox的初始化逻辑,排除其他干扰因素
可行解决方案
1. 用rememberUpdatedState稳定回调
如果回调函数是重组的诱因,用rememberUpdatedState包裹可以避免因外部状态变化触发的重组:
val onExpandedChange = rememberUpdatedState { expanded = !expanded } ExposedDropdownMenuBox( modifier = modifier, expanded = expanded, onExpandedChange = { onExpandedChange.value() } ) { TextField( modifier = modifier.menuAnchor(), value = "name", onValueChange = { }, ) }
2. 封装成独立Composable,用remember锁死内部状态
把下拉框和输入框单独抽成一个Composable,内部状态用remember保存,确保只有状态本身变化时才重组:
@Composable fun DropdownTextField( options: List<String>, modifier: Modifier = Modifier ) { var expanded by remember { mutableStateOf(false) } var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") } ExposedDropdownMenuBox( modifier = modifier, expanded = expanded, onExpandedChange = { expanded = !expanded } ) { TextField( modifier = modifier.menuAnchor(), value = selectedOption, onValueChange = { selectedOption = it }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) } ) ExposedDropdownMenu( expanded = expanded, onDismissRequest = { expanded = false } ) { options.forEach { option -> DropdownMenuItem( text = { Text(option) }, onClick = { selectedOption = option expanded = false } ) } } } }
之后在滚动Column里直接调用这个Composable就行,滚动时不会再触发不必要的重组。
3. 排查modifier传递问题
示例里ExposedDropdownMenuBox和TextField都直接用了外部的modifier,如果这个外部modifier包含了和滚动相关的动态状态(比如滚动偏移的计算),也会触发重组。可以换成具体的修饰符,不要直接传外部可能变化的modifier:
ExposedDropdownMenuBox( modifier = Modifier.fillMaxWidth(), expanded = expanded, onExpandedChange = { expanded = !expanded } ) { TextField( modifier = Modifier.menuAnchor().fillMaxWidth(), value = "name", onValueChange = { }, ) }
内容的提问来源于stack exchange,提问作者Illytax
相关产品推荐
相关产品推荐

