You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Column垂直滚动时ExposedDropdownMenuBox及关联TextField频繁重组问题

问题:带menuAnchor的ExposedDropdownMenuBox在滚动Column时频繁重组

现象

给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:20:31