我可以通过 Compose Multiplatform 中的单独按钮更改组件的值吗?

Can I change the value of a component from a separate button in Compose Multiplatform?

我正在尝试制作一个桌面应用程序,允许您在单独的目录中搜索存储在 Kotlin 类 中的多个预定义位置。为此,我使用了 reflections and compose-jb 库。

我 运行 遇到的问题是,当我输入标签后按下搜索按钮时,我无法弄清楚如何更新方框列(位于另一个方框组件中)以进行更改我要搜索。

下面是我的代码(针对 Main.kt 文件),它描述了整个桌面应用程序。

val reflections = Reflections("io.github.mobomega.project.attractions")
var display = mutableSetOf<Attraction>()

fun main() = application {
    val stateVertical = rememberScrollState(0)
    val stateHorizontal = rememberScrollState(0)
    var state = Box(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(stateVertical)
            .padding(end = 12.dp, bottom = 12.dp)
            .horizontalScroll(stateHorizontal)
    )
    Window(
        onCloseRequest = ::exitApplication,
        title = "Search",
        state = rememberWindowState(width = 2256.dp, height = 1504.dp)
    ) {
        val count = remember { mutableStateOf(1) }
        MaterialTheme {
            Column {
                val text = remember { mutableStateOf("") }
                OutlinedTextField(
                    value = text.value,
                    singleLine = true,
                    onValueChange = { text.value = it },
                    modifier = Modifier.align(Alignment.CenterHorizontally)
                )
                Row (modifier = Modifier.size(2256.dp, 50.dp), horizontalArrangement = Arrangement.Center) {
                    Button(modifier = Modifier.align(Alignment.Top),
                        onClick = {
                            val tags = text.value.split(", ", ",")
                            for (tag in tags) {
                                search(tag.lowercase())
                                println("$display have tag $tag")
                            }
                            // Setting the new value of the Box
                            state = create(stateVertical, stateHorizontal)
                            // Creates error:
                            // "@Composable invocations can only happen from the context of a @Composable function"

                        }) {
                        Text("Search")
                    }
                    Button (modifier = Modifier.align(Alignment.Top),
                        onClick = {
                            display.clear()
                        }) {
                        Text("Reset")
                    }
                }
                Row (horizontalArrangement = Arrangement.Center) {
                    Box(
                        modifier = Modifier.fillMaxSize()
                            .background(color = Color(red = 0xFF, green = 0xFF, blue = 0xFF))
                            .padding(10.dp)

                    ) {

                        state // Creating the state Box component in the Row

                        VerticalScrollbar(
                            modifier = Modifier.align(Alignment.CenterEnd)
                                .fillMaxHeight(),
                            adapter = rememberScrollbarAdapter(stateVertical)
                        )
                        HorizontalScrollbar(
                            modifier = Modifier.align(Alignment.BottomStart)
                                .fillMaxWidth()
                                .padding(end = 12.dp),
                            adapter = rememberScrollbarAdapter(stateHorizontal)
                        )
                    }
                }
            }
        }
    }
}

@Composable
fun textBox(text: String = "Item") {
    Box(
        modifier = Modifier.height(32.dp)
            .width(400.dp)
            .background(color = Color(200, 0, 0, 20))
            .padding(start = 10.dp),
        contentAlignment = Alignment.CenterStart
    ) {
        Text(text = text)
    }
}

@Composable
fun create(stateVertical: ScrollState, stateHorizontal: ScrollState) = Box(
    modifier = Modifier
        .fillMaxSize()
        .verticalScroll(stateVertical)
        .padding(end = 12.dp, bottom = 12.dp)
        .horizontalScroll(stateHorizontal)
    ) {
        Column {
            var x = 0
            for (attr in display) {
                x++
                textBox(attr.name)
                if (x < display.size) {
                    Spacer(modifier = Modifier.height(5.dp).align(Alignment.CenterHorizontally))
                }
            }
        }
}

fun search(text: String) {
    for (attr in reflections.getSubTypesOf(Attraction::class.java)) {
        val temp = attr.getConstructor().newInstance()
        println("${temp.name} has tags ${temp.tags}")
        if (temp.matches(text) && (temp !in display)) {
            display += temp
        }
    }
}

我已尝试更新包含所有符合任何搜索条件的项目的 Box 的值,但我 运行 遇到了一些问题,例如“onClick”功能我在其中设置了“状态”变量的新值(存储所有匹配项)不是可组合函数,因此我无法更改该值。

如何从另一个组件(例如 Button)更改 Component(例如 Box)的值?

在 Compose 中,您无法像使用 state 变量那样创建视图。您调用的结果只是 Unit,当您稍后调用它时,您应该会看到一条警告“表达式未使用”。创建变量时,视图将添加到树层次结构中。

要解决您的问题,您需要将 display 声明为可变状态 - 这是专为 Compose 制作的新事物,它允许在此状态更改时触发重组:

val display by mutableStateOf<Attraction>(setOf())

然后在您的 search 中像这样更新:

val mutableDisplay = mutableSetOf<Attraction>()
// for 
// ...
mutableDisplay += temp
// ...
display = mutableDisplay

请注意,您不能在可变状态中使用可变集,因为可变状态将无法跟踪此集的更改。

要了解有关 Compose 中的状态的更多信息,我建议您查看 this youtube video which explains the basic principles, and Compose mental model 以更好地了解如何使用它。