Jetpack Compose MutableState 未在屏幕上更新

Jetpack Compose MutableState not updating on the screen

我想显示来自 Firebase 实时数据库的电影列表,但我没有从 ViewModel 中获取列表。 当我更新存储库中的 apiResource.data 时,它不会更新屏幕上的 mutableList。

屏幕:

           val movies = viewModel.data.value.data?.toMutableList()

           if (viewModel.data.value.loading == true) {
               CircularProgressIndicator()
           } else {
              
               movies?.forEach {
                   Log.d("Movies", it.name)  // I'm not getting the movie list on the screen
               }

           }

视图模型:

val data: MutableState<ApiResource<List<Movie>, Boolean, Exception>> = mutableStateOf(
        ApiResource(null, true, Exception(""))
    )

    private fun getMovies() {

        viewModelScope.launch {
            data.value.loading = true
            data.value = repository.getAllMovies()

            if (data.value.data.toString() != "[]") {
                data.value.loading = false
            }
        }
    }

存储库:

class Repository {
    private val apiResource = ApiResource<List<Movie>,Boolean, Exception>()

    suspend fun getAllMovies(): ApiResource<List<Movie>, Boolean, java.lang.Exception> {

        try {
            apiResource.loading = true

            var movies: List<Movie>

            Firebase.database.reference.child("movies").addValueEventListener(object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    movies = dataSnapshot.children.map { snapshot ->
                        snapshot.getValue(Movie::class.java)!!
                    }

                    apiResource.data = movies  // set data in apiResource
                    if (apiResource.data.toString() != "[]") {
                        apiResource.loading = false
                    }

                }

                override fun onCancelled(error: DatabaseError) {}
            })
        } catch (exception: java.lang.Exception) {
            apiResource.e = exception
        }
        
        return apiResource
    }
}

Compose 无法跟踪可变状态包含的对象的更改。它只能跟踪对可变状态值的更新。

可能的解决方案:

  1. 通过创建具有更新属性的对象副本来更新您的可变状态。数据 class 在这种情况下非常方便:

    data class ApiResource(val isLoading: Boolean = false)
    
    class VM : ViewModel() {
        var apiResource by mutableStateOf(ApiResource())
    
        fun update() {
            apiResource = apiResource.copy(isLoading = true)
        }
    }
    
  2. 制作 ApiResource 可变状态的必要属性。

    class ApiResource {
        var isLoading by mutableStateOf(false)
    }
    
    class VM: ViewModel() {
        val apiResource = ApiResource()
    
        fun update() {
            apiResource.isLoading = true
        }
    }
    

您的代码的第二个问题是,您将协程与异步代码混为一谈。 addValueEventListener 不会等到 onDataChange 被调用,所以 getAllMovies 总是 returns 一个空对象。

要让协程等待异步调用完成,您需要使用 suspendCoroutine.

此外,如果结果发生变化,提供给 addValueEventListener 的侦听器可以多次调用。您当前的代码不希望这样,我建议您改用 addListenerForSingleValueEvent

val movies = suspendCoroutine<List<Movie>> { continuation ->
    Firebase.database.reference.child("movies").addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {

            continuation(
                dataSnapshot.children.map { snapshot ->
                    snapshot.getValue(Movie::class.java)!!
                }
            )
        }

        override fun onCancelled(error: DatabaseError) {}
    })
}