Thread
#compose
    Issa

    Issa

    1 year ago
    While taking the
    Pathway
    first code lab
    Compose Basics
    section
    Animating your list
    I have noticed that
    LazyColumn
    doesn’t remember the
    isSelected
    state of the item when recomposing. I have copy pasted the code from the
    Pathway
    and got the same results. Just when you scroll until the item is no more visible, when you scroll back the
    isSelected
    state is lost. IMO this is wrong cuz the state should be cached anyway and when recomposing the compiler should consider the previous state.Should I
    Hoist
    the state of the
    Greeting
    widget so I can manage it in the
    LazyColumn
    scope or this is just a Compose
    Bug
    ?
    🤔 Thanks in advance!
    jim

    jim

    1 year ago
    Hoisting state is never a bad idea 😉
    Issa

    Issa

    1 year ago
    Well that’s not efficient in my opinion 😕 even though it seems like a better idea however, a map is needed to sync the state of the items and their
    isSelected
    state -> added complexity
    @jim So here is what I did, I hoisted the state to the caller scope
    NamesList
    in this case. Now
    LazyColumn
    is not recomposing the items if the
    state
    changes, It only does when the item is being visible on the screen again.
    @Composable
    fun NameList(names: List<String>, modifier: Modifier = Modifier) {
        val selectedStates = remember { mutableStateOf(mutableMapOf<Int, Boolean>()) }
        val map = mutableMapOf<Int, Boolean>()
        names.forEachIndexed { index, _ ->
            map[index] = false
        }
        selectedStates.value = map
        LazyColumn(modifier = modifier) {
            itemsIndexed(items = names) { index, name ->
                val i: Boolean = selectedStates.value[index] ?: false
                Greeting(
                    name = name,
                    isSelected = i,
                    onSelected = {
                        selectedStates.value[index] = !it
                    }
                )
                Divider(color = Color.Black)
            }
        }
    }
    jim

    jim

    1 year ago
    Sorry the API not very discoverable. You'd want to use
    mutableStateMapOf
    instead of
    mutableMapOf
    , else Compose won't know when the map is mutated.
    Issa

    Issa

    1 year ago
    @jim Thanks! that did the trick 😉 As far as I understand
    remember{}
    remembers the state for only one composition invocation, however not
    recomposition
    done by the
    LazyFoo
    apis, cuz these guys they unsubscribe from the previous and init a new invocation which yields a fresh state. So far here is the code if you want to update the
    Pathway
    code lab.
    @Composable
    fun NameList(names: List<String>, modifier: Modifier = Modifier) {
    
        val selectedStates = remember {
            mutableStateMapOf<Int, Boolean>().apply {
                names.mapIndexed { index, _ ->
                    index to false
                }.toMap().also {
                    putAll(it)
                }
            }
        }
    
        LazyColumn(modifier = modifier) {
            itemsIndexed(items = names) { index, name ->
                Greeting(
                    name = name,
                    isSelected = selectedStates[index] == true,
                    onSelected = {
                        selectedStates[index] = !it
                    }
                )
                Divider(color = Color.Black)
            }
        }
    }
    Thanks again for your help 😉
    Jorkoh

    Jorkoh

    1 year ago
    It's called the "Compose basics" codelab because it's about the basics, the "Using State in Compose" delves into the hoisting pattern also with selected stuff in a lazyColumn. Anyway you don't even need a map, just a list of the selected items or their ids
    Andrey Kulikov

    Andrey Kulikov

    1 year ago
    also if you will use
    rememberSaveable
    instead of
    remember
    the state would survive the item being scrolled off and on
    Issa

    Issa

    1 year ago
    I will give
    rememberSaveable
    api a try this sounds really interesting!! @Andrey Kulikov
    @Andrey Kulikov That works like a charm! Although reading the docs of
    rememberSaveable()
    It behaves similarly to remember, but the stored value will survive the activity or process recreation using the saved instance state mechanism (for example it happens when the screen is rotated in the Android application).
    I am wondering if abusing this patter with say 1000 items in a list where each item is using
    rememberSaveable
    under the hood, I am wondering if this has any performance penalties? 🤔 Other than that… I loved the idea and it was exactly what I was looking for! Thanks again!
    I also noticed that it doesn’t work in Interactive Preview mode but maybe this is something to be fixed in the future versions of compose!
    Andrey Kulikov

    Andrey Kulikov

    1 year ago
    yes, this value will also be restored when the activity would be recreated. no performance penalty, but a bit of an extra memory to be kept, like 1000 booleans in this case
    but only if you will really scroll over 1000 items
    Issa

    Issa

    1 year ago
    That’s interesting, I wonder what would be the solution of implementing a viewHolder for the list, that’s something i will look into, maybe there is a way to achieve something similar to recyclerView.
    Andrey Kulikov

    Andrey Kulikov

    1 year ago
    Sorry, what do you mean? Why do you need a ViewHolder?
    Issa

    Issa

    1 year ago
    In real world cases, the state of an item is not just
    isSelected
    state, it might be more complex depending on the UI. So I thought maybe by creating a
    ViewHolder
    like API, it would be easier to hold the state. I am not sure if this is the responsibility of the item itself or the
    LazyColumn