我有一个可变状态列表,我想通过 lambda 从单独的可组合函数进行更新。该代码使用新数据更新列表,但不会重构 UI。
这应该做什么:
单击按钮时,打开两个文本字段的叠加层以编辑可变状态列表中的信息。 完成后,关闭叠加层并根据叠加层中输入的信息更新 UI。
概念相当简单,但重组是问题所在。
val groupComponents = remember {
mutableStateListOf<GroupedComponents>()
}
val boxVisible = remember {
mutableStateOf(false)
}
val boxElementIndex = remember {
mutableIntStateOf(0)
}
val boxGroupIndex = remember {
mutableIntStateOf(0)
}
val boxName = remember {
mutableStateOf("")
}
val boxCommand = remember {
mutableStateOf("")
}
EditCustomUIBoxOverlay(
boxVisible,
boxName,
boxCommand,
close = {
boxVisible.value = !boxVisible.value
},
processBoxData = { name, command ->
val list = groupComponents[boxGroupIndex.value].elements
list[boxElementIndex.value].name = name.value
list[boxElementIndex.value].data = command.value
groupComponents[boxGroupIndex.value].copy(
elements = list
)
})
@Composable
fun EditCustomUIBoxOverlay(
boxVisible: MutableState<Boolean>,
boxName: MutableState<String>,
boxCommand: MutableState<String>,
processBoxData: (MutableState<String>, MutableState<String>) -> Unit,
close: () -> Unit
) {
AnimatedVisibility(
visible = boxVisible.value,
enter = fadeIn(),
exit = fadeOut()
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = Color(0.0f, 0f, 0f, 0.5f)),
contentAlignment = Alignment.Center
) {
ElevatedCard(
modifier = Modifier
.padding(15.dp)
.defaultMinSize()) {
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(value = boxName.value, onValueChange = { it ->
boxName.value = it
}, label = { Text(text = "Button Name") })
}
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(value = boxCommand.value, onValueChange = { it ->
boxCommand.value = it
}, label = { Text(text = "Enter Command") })
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Button(onClick = {
close()
processBoxData(boxName, boxCommand)
}) {
Text(text = "Done")
}
Button(onClick = { close() }) {
Text(text = "Cancel")
}
}
}
}
}
}
如果我跳过单独创建新的可组合函数并直接添加代码,它就会按照我想要的方式工作。它更新可变状态列表并根据更改重新组合 UI。
以下作品
AnimatedVisibility(
visible = boxVisible.value,
enter = fadeIn(),
exit = fadeOut()
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = Color(0.0f, 0f, 0f, 0.5f)),
contentAlignment = Alignment.Center
) {
ElevatedCard(
modifier = Modifier
.padding(15.dp)
.defaultMinSize()) {
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(value = boxName.value, onValueChange = { it ->
boxName.value = it
}, label = { Text(text = "Button Name") })
}
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(value = boxCommand.value, onValueChange = { it ->
boxCommand.value = it
}, label = { Text(text = "Enter Command") })
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Button(onClick = {
boxVisible.value = !boxVisible.value
val list = groupComponents[boxGroupIndex.value].elements
list[boxElementIndex.value].name = boxName.value
list[boxElementIndex.value].data = boxCommand.value
groupComponents[boxGroupIndex.value].copy(
elements = list
)
}) {
Text(text = "Done")
}
Button(onClick = { close() }) {
Text(text = "Cancel")
}
}
}
}
}
使用 lambda 回调创建单独的函数是否会失去按预期访问可变状态列表的能力?
我尝试过在可组合函数内部和外部移动变量,移动调用函数的位置,更改可组合函数中的参数。所有这些都给出了相同的问题,数据根据需要进行更改,但 UI 不会重新组合。
在 Jetpack Compose 中,您永远不应该将
MutableState<T>
作为参数传递给其他可组合项,因为这违反了 单向数据流 模式。在您的代码中,数据在可组合层次结构中向上流动。
相反,您应该传递实际值
T
,当子 Composable 想要更新 T
时,它会使用回调函数通知父 Composable 应设置的新值。
在你的情况下,它看起来像这样:
@Composable
fun EditCustomUIBoxOverlay(
boxVisible: Boolean,
boxName: String,
boxCommand: String,
processBoxData: (newName: String, newCommand: String) -> Unit,
close: () -> Unit
) {
var updatedBoxName by remember { mutableStateOf(boxName) }
var updatedBoxCommand by remember { mutableStateOf(boxCommand) }
AnimatedVisibility(
visible = boxVisible,
enter = fadeIn(),
exit = fadeOut()
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = Color(0.0f, 0f, 0f, 0.5f)),
contentAlignment = Alignment.Center
) {
ElevatedCard(
modifier = Modifier
.padding(15.dp)
.defaultMinSize()) {
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(
value = updatedBoxName,
onValueChange = { it ->
updatedBoxName = it
},
label = { Text(text = "Button Name") })
}
Row(
modifier = Modifier.defaultMinSize(),
horizontalArrangement = Arrangement.Center
) {
TextField(
value = updatedBoxCommand,
onValueChange = { it ->
updatedBoxCommand = it
},
label = { Text(text = "Enter Command") })
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Button(
onClick = {
processBoxData(updatedBoxName, updatedBoxCommand)
close()
}
) {
Text(text = "Done")
}
Button(onClick = { close() }) {
Text(text = "Cancel")
}
}
}
}
}
}
然后,从父 Composable 中,您可以这样调用它:
@Composable
fun MainComposable() {
var boxVisible by remember { mutableStateOf(true) }
var boxName by remember { mutableStateOf("") }
var boxCommand by remember { mutableStateOf("") }
EditCustomUIBoxOverlay(
boxVisible = boxVisible,
boxName = boxName,
boxCommand = boxCommand,
processBoxData = { newBoxName, newBoxCommand ->
boxName = newBoxName,
boxCommand = newBoxCommand
},
close = {
boxVisible = false
}
)
}