Jetpack Compose not updating without LaunchedEffect of unrelated item

185 views Asked by At

Please can someone throw any light on the problem that I have with Jetpack Compose and a ViewModel.

I have a data class called ProductEntity.

data class ProductEntity(
    var id: Int = 0,
    var name: String = "",
    var quant: Int = 1,

    )

In a ViewModel I have:

  1. private local list of products,
  2. Boolean button state and
  3. a Products list which is a MutableState<List<ProductEntity>>. This is set to start with the values from the local list of products
  4. a function to update the product list based on a counter number passed
class TestGlobalViewModel : ViewModel() {

    private val localList = ArrayList<ProductEntity>().apply {
        add(ProductEntity(name = "a"))
        add(ProductEntity(name = "b"))
        add(ProductEntity(name = "c"))
    }

    var buttonState by mutableStateOf(false)

    var products = mutableStateOf<List<ProductEntity>>(
            localList
    )

    fun updateProducts(counter:Int){
        val localList2 = ArrayList<ProductEntity>()
        localList2.add(ProductEntity(name = "a$counter"))
        localList2.add(ProductEntity(name = "b$counter"))
        localList2.add(ProductEntity(name = "c$counter"))
        localList2.add(ProductEntity(name = "a${counter + 1}"))
        localList2.add(ProductEntity(name = "b${counter + 1}"))
        localList2.add(ProductEntity(name = "c${counter + 1}"))
        localList2.add(ProductEntity(name = "a${counter + 3}"))
        localList2.add(ProductEntity(name = "b${counter + 3}"))
        localList2.add(ProductEntity(name = "c${counter + 3}"))

        products = mutableStateOf(localList2)
    }

}

I have the HomeScreen Composable

In there I have:

  1. a reference to the ViewModel
  2. mutable state of a counter
  3. mutable state of a filtered list. This latter list is updated whenever the Products list is replaced.

I have a Button that displays the first item in the filtered list and does three things when pressed

  1. Increments the counter
  2. updates the product list
  3. toggles the buttonState variable in the view model

Finally I have a LaunchedEffect triggered by the button state that does nothing.


@Composable
fun HomeScreen() {

    val vm = viewModel<TestGlobalViewModel>()

    var counter by remember {
        mutableStateOf(1)
    }
    var filteredList by remember {
        mutableStateOf<List<ProductEntity>>(emptyList())
    }

    filteredList = run {
        val filtered = ArrayList<ProductEntity>()
        vm.products.value.forEach {
            if (it.name.contains("a")) filtered.add(it)
        }
        filtered
    }

    LaunchedEffect(vm.buttonState) {}

    TestTheme {
        Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
        ) {
            Button(onClick = { // temporary print
                counter += 1
                vm.updateProducts(counter)
                Log.i("CEtest", // print out the filtered list
                      buildString {
                          filteredList.forEach {
                              append("filteredList = $it \n")
                          }
                      })
                vm.buttonState = vm.buttonState == false
                }) {
                Text(text = filteredList[0].name) 
            }

        }
    }
}

This works perfectly until either the LaunchedEffect is removed or the button state is not changed. At that point nothing works - the button text is not updated and the filtered list is not changed.

I cannot see how this is occurring and why I need the button state LaunchedEffect to trigger the rest of the process.

Any help you can give would be gratefully appreciated.

Thanks

0

There are 0 answers