How would you ideally represent an observable list...
# compose
j
How would you ideally represent an observable list of data, where each element also has a state that can be updated. In case a single element updates its state, the list state should ideally not be updated to avoid unnecessary recomposition, etc I suppose you can do better than a
mutableStateListOf<StateFlow<StateFulData>>()
or similar?
o
I personally would avoid
mutableStateListOf<T>
in favour of
mutableStateOf<List<T>
for the same reason I would avoid
val list: MutableList<T>
in favour of
var list: List<T>
. Then, everytime a single element updates its state, you need to create a new list with the updated element and do
currentStateList = newList
j
Yeah but there is UI state associated to the element that i could not keep if i would generate an entirely new list. Think of it has an item that can be expanded. If you just give it a new list, the expanded state would be reset for each element, even if its not the element with updated state --> bad ux
o
Does your list own the state of whether an item is expanded?
Either way, your list should know which item(s) is/are currently expanded, then you can just copy the list when you are updating an element so that you can still secure the expanded state.
j
I do not want to go this route. There are advantages to immutable lists etc, but this creates more problems than it solves, at least imo
o
Alternative is that you could keep your mutabeStateListOf and introduce a seperate state, something like var expandedItemId: Int = ...
j
That would still trigger recomposition for the entire list an everything, not a fan
o
You mean you expect Compose not being smart enough to just rerender the changed part?
j
I want to keep the Composables alive so it doesnt have to reinitialize all the states, and I do not at all think that having a list of states would be a good idea by any stretch of the imagination.
o
I think you are going too technical and not embracing Compose's declarative advantage. Thinking about initialization of UI is still part of the old imperative style of UI programming
Compose should take care of the performance. Don't think about efficient UI mutation, just tell Compose what your new desired UI is, and Compose should be able to efficiently handle it.
j
Oh i know how compose works... But having a list of expandedItems sounds terrible by architecture. Having an immutablelist that recomposes everything means losing local state such as isExpanded etc
o
Could you show some code? I'm not fully following what kind of list / UI we are talking about
j
I suppose this code example would show what I need
Copy code
sealed class StatefulData {
    class Stage1(val msg: String): StatefulData()
    class Stage2(val t: Throwable): StatefulData()
}
@Composable
fun StateExample() {
    val list = mutableStateListOf<StateFlow<StatefulData>>()
    list.forEach {
        var (extended, setExtended) = remember { mutableStateOf(false) }
        Card(Modifier.toggleable(extended, onValueChange = setExtended)) {
            Column {

// Collecting as state: when the StateFlow for a single element updates, a single element is recomposed, 
// without losing the associated extended state

                when (val el = it.collectAsState().value) {
                    is StatefulData.Stage1 -> {
                        Text(el.msg, color = Color.Green)
                    }
                    is StatefulData.Stage2 -> {
                        Text(el.t.message ?: "No Error Message", color = Color.Red)
                    }
                }

                AnimatedVisibility(visible = extended) {
                    Text("Showing information in more detail")
                }
            }
        }
    }
}
o
Flow inside state is a no-go
j
Yes, that is my feeling too, but I don't see anything better
o
Embrace the immutable path
j
... then I will have to map
extended
states to elements in some way, that is also terrible
o
It isn't, it's just you specifying / being explicit in what is extended or not
j
and managing those states / syncing these states? Not a fan... Not like my idea is better
o
You don't need to sync state. Syncing state is an anti pattern by itself.
j
how would you keep track of what is extended?
o
Move the isExtended state to the root of the Composable, and make it extendedId, or extendedIds if its possible for multiple cards to be expanded at the same time
j
well,, that is syncing state though.... Removing states if tey disappear, adding states when they appear
o
Could you elaborate?