如何使用 Jetpack Compose 将异步操作的响应传播到视图?

How to propagate the response of an async operation to the view using Jetpack Compose?

我把这个封起来了class:

sealed class Resource<out T> {
    object Loading: Resource<Nothing>()
    data class Success<out T>(val data: T): Resource<T>()
    data class Failure(val message: String): Resource<Nothing>()
}

在存储库 class 中,我有这个功能可以从 API:

中删除一个项目
override suspend fun deleteItem(id: String) = flow {
    try {
        emit(Resource.Loading)
        emit(Resource.Success(itemsRef.document(id).delete().await()))
    } catch (e: Exception) {
        emit(Resource.Failure(e.message))
    }
}

删除操作的结果是Void?。现在,在 ViewModel class 中我声明:

val state = mutableStateOf<Resource<Void?>>(Success(null))

并在删除完成后更新它:

fun deleteItem(id: String) {
    viewModelScope.launch {
        repo.deleteItem(id).collect { response ->
            state.value = response
        }
    }
}

我创建了一张卡片并在 onClick 中添加了:

IconButton(
    onClick = viewModel.deleteItem(id),
)

这实际上从数据库中正确删除了该项目。但是我无法跟踪操作的结果。我尝试使用:

when(val res = viewModel.state.value) {
    is Resource.Loading -> Log.d(TAG, "Loading")
    is Resource.Success -> Log.d(TAG, "Success")
    is Resource.Failure -> Log.d(TAG, "Failure")
}

但只有案例Loading被触发。根本没有 success/failure。这里有什么问题?因为它真的像一个同步操作。

尝试在可组合函数中收集:

val state = viewModel.state.collectAsState()

那么你可以这样做: when (val res = viewModel.state.value){...}.

但是我对存储库中返回 flowdeleteItem 持怀疑态度。你真的需要这样的东西吗?您总是可以 map viewModel 中的内容。

我已经在没有存储库的情况下测试了你的方法,组成部分看起来完全没问题:

var i = 0

@Composable
fun TestScreen(viewModel: TestViewModel = viewModel()) {
    val state by viewModel.state
    Text(
        when (val stateSmartCast = state) {
            is Resource.Failure -> "Failure ${stateSmartCast.message}"
            Resource.Loading -> "Loading"
            is Resource.Success -> "Success ${stateSmartCast.data}"
        }
    )
    Button(onClick = {
        viewModel.deleteItem(++i)
    }) {

    }
}

class TestViewModel : ViewModel() {
    val state = mutableStateOf<Resource<Int>>(Resource.Success(i))

    fun deleteItem(id: Int) {
        viewModelScope.launch {
            deleteItemInternal(id).collect { response ->
                state.value = response
            }
        }
    }

    suspend fun deleteItemInternal(id: Int) = flow {
        try {
            emit(Resource.Loading)
            delay(1000)
            if (id % 3 == 0) {
                throw IllegalStateException("error on third")
            }
            emit(Resource.Success(id))
        } catch (e: Exception) {
            emit(Resource.Failure(e.message ?: e.toString()))
        }
    }
}

所以问题看起来像在这一行 itemsRef.document(id).delete().await()) 中,或者在您与存储库的连接中。