Moving Window from one screen to the other duplicates items in itemsIndexed - Compose Desktop

63 views Asked by At

I am having trouble implementing best practices for LazyLists and itemsIndexed when adding new items into the list through user input, buttons.

It has been suggested that I am using the wrong way of adding new items into the list. If there could be a clear explanation why I am not able to recomposition the items properly, without duplicates.

The idea would be that I have the initial list, then I could add new items to that list and that the list would update appropriately.

There are maybe 2 different problems:

  • 1 - I am having difficulty in understanding how compose is able to update new items into the mutablestatelistof.

    • Either through a mutable state variable (where the recomposition happens) or within the intended list variable itself (then the recomposition won't happen, etc.).

    • How am I able to sync these two variables, so that after the add/remove actions have happened the actual list is updated correctly and the UI is recompositioned to the correct state?

  • 2 - Before and after I am able to add items, when I move the application window from one window to the other there becomes duplicates. The duplicates will add the list contents back into the list.

    • What is causing this?

    • How am I better able to develop the code in order to prevent this?

Reduced Example Code - Add Button will create a new object and update list:

TestObject.kt

data class TestObject(var testName: String)

Main.kt

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application

@Composable
@Preview
fun App() {
    var text by remember { mutableStateOf("Hello, World!") }
    val listSorted = remember {
        mutableStateListOf<TestObject>()
    }
    val testObjectOne = TestObject("Test One")
    val testObjectTwo = TestObject("Test Two")

    listSorted.add(testObjectOne)
    listSorted.add(testObjectTwo)

    MaterialTheme {
        Button(onClick = {
            text = "Hello, Desktop!"
            listSorted.add(TestObject("New Object"))
        }) {
            Text(text)
        }

        LazyColumn {
            itemsIndexed(listSorted) { index, item ->
                Text(item.testName)
            }
        }
    }
}

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        App()
    }
}

In my actual code, I have used .clear() and then .addAll(list) in order to add back to the list. I expect though that I should be adding directly into the list variable only and then somehow getting the variable to recomposition correctly. Not sure how this can be done.

This example seems to work for my intended application anyways.

  • Add new items, list is fully visible - OK

  • Move application to new window, initial list items are duplicated - NOK

1

There are 1 answers

0
Timo Drick On

I think you code is almost correct. You just should not add elements to the list at a compose function. So change you code to:

...
val listSorted = remember {
    mutableStateListOf<TestObject>()
}
LaunchedEffect(Unit) {
    val testObjectOne = TestObject("Test One")
    val testObjectTwo = TestObject("Test Two")
    listSorted.add(testObjectOne)
    listSorted.add(testObjectTwo)
}
MaterialTheme {
...