Home > Enterprise >  Why does Jetpack Compose LazyColumn MutableLiveData only recompose on first button click?
Why does Jetpack Compose LazyColumn MutableLiveData only recompose on first button click?

Time:05-12

Learning Kotlin and Jetpack Compose in Android Studio. Trying to understand recomposition.

The following code only recomposes on the first button click - why?

Output is shown below my code. Basically, the first time I click the Add New Item button the screen recomposes perfectly, but then it does not recompose again. However, if you look at the LogCat, you can see that it's still working, just not updating the screen. What am I missing?

View Model

class MainViewModel : ViewModel() {
    val listLiveData: LiveData<List<Int>>
        get() = newListLiveData

    private val newList = ArrayList<Int>()
    private val newListLiveData = MutableLiveData<List<Int>>()

    fun addNewListItem(listItem: Int) {
        newList.add(listItem)
        newListLiveData.value = newList
        println("**** addNewListItem($listItem)")
    }
}    

Home Screen Scaffolding and Content

@Composable
fun HomeScreen(
    navController: NavHostController,
    model: MainViewModel
) {

    val stuff by model.listLiveData.observeAsState(emptyList())

    Scaffold(
        content = { Content(model, stuff) }
    )

}

@Composable
fun Content(
    model: MainViewModel = MainViewModel(),
    stuff: List<Int>
) {
        LazyColumn() {
            items(items = stuff){ index ->
                Text("$index")
            }
        }
        Button(
            onClick = { model.addNewListItem((0..10).random()) },
        ) {
            Text(text = "Add Random Item to List")
        }
}

LogCat Output

I/System.out: **** addNewListItem(0)  <-- Recomposes fine here 
I/System.out: **** addNewListItem(2)  <-- Does not recompose
I/System.out: **** addNewListItem(5)  <-- Does not recompose
I/System.out: **** addNewListItem(6)  <-- Does not recompose
I/System.out: **** addNewListItem(4)  <-- Does not recompose

Screenshot

enter image description here

Thank you

CodePudding user response:

Honestly speaking, as long as you do not have to do with interoperability with the View system, AVOID using anything other than the built-for-Compose MutableState<T> objetcs. The ultra-boilerplate code of yours can simply be replaced by

ViewModel{
 val cleanList by mutableStateListOf<Item>() // Initialized as an Empty list of 'Item's
 fun addItem(item: Item){
  cleanList.add
 }
}

Within Composables,

@Composable
MyComposable(
 list: List<Item>,
 onAddItem: (Item) -> Unit // Receive Item, return Unit
) {
 Button { // This is on-Click, assume
  onAddItem(/*Add Item Here, per logic*/)
 }
}

Call it anywhere like so

MyComposable(list = viewModel.cleanList, onAddItem = viewModel::addItem)

This is some fine code here. Easy to read, free of boilerplate, bafflingly beautiful and clean as a crystal.

Leverage the beauty of Compose, and ditch the ugly legacy crap.

  • Related