kotlin Android Jetpack Compose mutableStateListOf not doing Recomposition

b4qexyjb  于 2023-04-12  发布在  Kotlin
关注(0)|答案(5)|浏览(375)

因此,我有一个viewModel中的mutableStateListOf

var childTravellersList = mutableStateListOf<TravellersDetails>()

TravellersDetails是具有称为error的字段的数据类。
这个childTravellersList在UI中的用法是:

val list = remember{viewModel.childTravellersList}

LazyColumn(state = lazyColumnState) {
    itemsIndexed(list) { index, item ->
        SomeBox(show = if(item.error) true else false)
    }
  }

我在viewModel中写了一个函数,它在childTravellersListindex处更新了TravellersDetails的error

fun update(index){
    childTravellersList[index].error = true
}

因此,每当我调用这个函数时,列表应该得到更新。
这会更新列表,但不会触发UI重组😕。我哪里做错了?

ygya80vv

ygya80vv1#

mutableStateListOf只能通知添加/删除/替换列表中的某些元素。当您更改列表中的任何类时,可变状态无法知道它。
数据类非常适合在单向数据流中存储不可变状态,因为当你看到需要将新数据传递给视图或可变状态时,你总是可以用copy“更改”它。所以避免将var变量与数据类一起使用,总是将它们声明为val以防止此类错误。

var childTravellersList = mutableStateListOf<TravellersDetails>()

fun update(index){
    childTravellersList[index] = childTravellersList[index].copy(error = true)
}

另一个问题是您正在使用val list = remember{viewModel.childTravellersList}:它保存第一个列表值并防止将来更新。通过ViewModel,您可以直接使用它itemsIndexed(viewModel.childTravellersList)

ryevplcw

ryevplcw2#

只有当你改变列表本身的时候才会发生重组。你可以这样做。

var childTravellersList by mutableStateOf(emptyList<TravellersDetails>())

fun update(indexToUpdate: Int) {
    childTravellersList = childTravellersList.mapIndexed { index, details ->
        if(indexToUpdate == index) details.copy(error = true)
        else details
    }
}

另外,你不需要像在val list = remember{viewModel.childTravellersList}中那样在composable中记住这个列表。由于MutableState在视图模型中,它将始终在所有重组中存活。只需在LazyColumn中使用viewModel.childTravellersList

ijnw1ujt

ijnw1ujt3#

使用单一触发机制来更新组合对象。您的列表应该仅作为普通变量可用。您可以添加/移除/删除列表中的项目,甚至更新每个项目的属性。一旦您对列表进行了这些更新,您可以通过生成一个随机数来重新组合组合对象,该随机数绑定到组合对象中观察到的可变状态:
Kotlin:

class MyViewModel: ViewModel() {
   var childTravellersList = mutableListOf<TravellersDetails>()
   var onUpdate = mutableStateOf(0)

   private fun updateUI() {
      onUpdate.value = (0..1_000_000).random()
   }

   fun update(index){
       childTravellersList[index].error = true
       updateUI()
   }
}

@Composable
fun MyComposableHandler() {

   // This will detect any changes to data and recompose your composable.
   viewmodel.onUpdate.value
  
   MyComposable(
      travelersList = viewmodel.childTravellersList
   )
}

@Composable
fun MyComposable(
    travelersList: List<TravellersDetails>
) {
   
}

你应该避免在你的viewmodel中为不同的需要更新的变量创建多个可变状态变量,完全没有必要这样做,只要创建一个单一的可变状态变量,如上所示,每当你需要重新组合你的composable你只需要updateUI函数。视图模型中的逻辑应该决定什么需要更新,但是把实际的更新机制留给一个可变的可观察状态。这是一个干净的模式,将使更新您的合成更容易。
但是,请记住,UI通常是由层次结构中的许多可组合项组成的。您不希望在只有一个元素发生更改时重新组合整个层次结构。因此,应该为每个需要独立于层次结构中的其他元素进行重新组合的可组合项使用可变状态Observable。
使用上面展示的解决方案的另一个好处是,你可以很容易地更新对象,而不需要创建新的对象,如果你想让你的composable在只有对象的某个属性发生变化时重新组合你不能使用一个可变的状态观察,因为他们只检测到对象本身的变化,而不是对象的属性。这就是为什么您最好使用上面显示的触发方法,并在可组合对象重新组合时简单地检索更新的对象。

ua4mk5z4

ua4mk5z44#

如果你不需要data class,你可以看看这个

1dkrff03

1dkrff035#

ViewModel中的State是一个非常好的教程,它解释了如何管理列表。下面的片段来自教程:
有两种方法可以解决此问题:
1.更改我们的数据类WellnessTask,使checkedState变为MutableState而不是Boolean,这会导致Compose跟踪项更改。
1.拷贝您要变异的项目,从列表中移除该项目,然后将变异的项目重新添加到列表中,这会使“撰写”跟踪该列表更改。

相关问题