Jetpack Compose: Update composable when list changes
Asked Answered
B

3

45

I've got a composable on a Screen which shows a list of Track items (favourites) :

var favourites: MutableList<Track> by mutableStateOf(mutableListOf())
@ExperimentalFoundationApi
@Composable
private fun ResultList(model: FreezerModel) {
    with(model) {
        if (favourites.isEmpty()) NoDataMessage("No favourites yet")
        else {
            LazyColumn(state = rememberLazyListState()) {
                items(favourites) {
                    TrackCard(it, model)
                }
            }
        }
    }
}

On click events, I am updating my favourites list (add/remove item). How can I make my composable reflect these changes immediately (like to re-draw itself or something similar)? So far, it only works when I first switch to another screen.

Thanks for your inputs!

Birdiebirdlike answered 25/4, 2021 at 10:50 Comment(3)
It should work with a MutableStateList<T>. Use var favourites by mutableStateListOf<Track>()Aphasic
Thanks for your input! Is this also possible for a Set? I can't find any implementation...Birdiebirdlike
Sets are not ordered and therefore do not implement List<T> nor Array<T> supported by items() and are therefore cannot be used as a parameter to items() directly. In this case, calling toList() on the set every time is updated would work.Menefee
A
76

You need to use a MutableStateList<T> so that Compose can automatically recompose when the state changes.

From official doc:

Caution: Using mutable objects such as ArrayList<T> or mutableListOf() as state in Compose will cause your users to see incorrect or stale data in your app.

In your code use

val favourites = remember { mutableStateListOf<Track>()}

instead of

var favourites: MutableList<Track> by mutableStateOf(mutableListOf())
Aphasic answered 25/4, 2021 at 11:38 Comment(8)
Awesome, thx again for your input! Is this also possible for a Set? I can't find any implementation...Birdiebirdlike
If I need list of list then?Valuer
In my case only var canteens by mutableStateOf(mutableStateListOf<Canteen>()) worksDanby
What about LivedData and StateFlow? https://mcmap.net/q/374321/-notify-livedata-observers-when-nested-properties-change/3466808.Lessielessing
We cannot use 'by' keyword with mutableStateListOf(). Use '=' symbol instead.Giuseppinagiustina
MutableStateList is no more available in AndroidMascarenas
Drove an explanation to another query, and it's functioning nicely for my case. You can have a look at this explanation https://mcmap.net/q/374322/-mutablestateflow-not-working-with-mutablelistGarlic
if MutableStateList is not available try mutableStateListOf instead, like mutableStateListOf<Card>() for instance.Bombacaceous
K
10

Just removing state = rememberLazyListState() from the lazyColumnFor params list should work in your case.

According to the doc if we use rememberLazyListState() then :-

Changes to the provided initial values will not result in the state being recreated or changed in any way if it has already been created.

After doing so , normally updating the list should work fine. Also its a good practice to expose an immutable list ( list ) instead of a mutableList to the composable.

For example use:-

var favourites by mutableStateOf(listOf<FavoriteItem>())

then add/remove/update the list by using :-

favourites = favourites + newList // add 
favourites = favourites.toMutableList().also { it.remove(item) } // remove
 
Kylix answered 25/4, 2021 at 11:40 Comment(2)
Thanks for your input! Is this also possible for a Set? I can't find any implementation...Birdiebirdlike
val setData = remember { mutableStateMapOf("1" to "Jayant","2" to "Manish") } Text(text = "${setData["1"]}")Inharmonious
I
3

If you want to use mutableStateMapOf(), you can do like this👇

    val setData = remember { mutableStateMapOf("1" to "Jayant","2" to "Manish") }

    Text(text = "${setData["1"]}")

Output

Jayant
Inharmonious answered 22/2, 2023 at 10:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.