You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Composable状态变更时isLoading未更新,CircularProgressIndicator不显示

问题:Compose中CircularProgressIndicator无法显示,Loading状态未更新

我定义了isLoading状态,期望其值为true时显示CircularProgressIndicator,对应的Composable代码如下:

@Composable
fun ProductDetailScreen(
    viewModel: ProductDetailViewModel = hiltViewModel()
) {
    val productState = viewModel.productState.value
    LazyColumn{
        item {
            if (productState.isLoading)
                CircularProgressIndicator()
        }
    }
}

我使用Resource类封装API调用结果,在Repository中用该类包装请求结果。问题在于,尽管Repository返回了Resource.Loading,但ViewModel中的isLoading状态并未更新,导致屏幕上的CircularProgressIndicator无法显示。


相关代码

Resource类

sealed class Resource<T>(
    val data: T? = null,
    val message: String? = null,
    val errorType: ExceptionMapper.Type? = null
) {
    class Success<T>(data: T?) : Resource<T>(data)
    class Error<T>(message: String, errorType: ExceptionMapper.Type, data: T? = null) : Resource<T>(data, message, errorType)
    class Loading<T>(isLoading: Boolean = true) : Resource<T>()
}  

Repository

override suspend fun getProductComments(productId: Int): Resource<List<Comment>> {
        return try {
            Resource.Loading<List<Comment>>()
            delay(3000)
            Resource.Success(apiService.getComments(productId))
        } catch (t: Throwable) {
            val mappedException = ExceptionMapper.map(t)
            Resource.Error(message = t.message!!, errorType = mappedException.type)
        }
    }    

ViewModel

@HiltViewModel
class ProductDetailViewModel @Inject constructor(
    state: SavedStateHandle,
    private val productRepository: ProductRepository
) : ViewModel() {

    private val passedProduct = state.get<Product>(EXTRA_KEY_DATA)
    var productId = passedProduct?.id

    var productState = mutableStateOf(ProductState())
        private set

    init {
        getProductComments()
    }
    private fun getProductComments() {
            viewModelScope.launch {
                productId?.let { pId ->
                    when (val commentResult = productRepository.getProductComments(pId)) {
                        is Resource.Success -> {
                            commentResult.data?.let { comments ->
                                productState.value =
                                    productState.value.copy(
                                        comments = comments,
                                        error = null,
                                        isLoading = false
                                    )
                            }
                        }
                        is Resource.Error -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = true,
                                isLoading = false,
                                error = commentResult.message
                            )
                        }
                        is Resource.Loading -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = false,
                                isLoading = true,
                                error = null
                            )
                        }
                    }
                }
            }
        }
}

问题原因分析

  1. Repository中Loading状态未实际传递
    你的Repository方法里,Resource.Loading<List<Comment>>()只是创建了一个实例,但没有返回它,紧接着执行delay(3000)后直接返回了Resource.Success。单个suspend函数只能返回一个值,所以Loading状态根本没有传递到ViewModel。

  2. 单个返回值无法承载多状态
    要在请求流程中依次发送Loading、Success/Error多个状态,不能用普通suspend函数返回单个Resource对象,必须使用Flow来发射多个状态值。


修正方案

1. 修改Repository,改用Flow发射状态

override fun getProductComments(productId: Int): Flow<Resource<List<Comment>>> = flow {
    // 先发射Loading状态
    emit(Resource.Loading<List<Comment>>())
    try {
        delay(3000)
        val comments = apiService.getComments(productId)
        // 发射Success状态
        emit(Resource.Success(comments))
    } catch (t: Throwable) {
        val mappedException = ExceptionMapper.map(t)
        // 发射Error状态
        emit(Resource.Error(message = t.message!!, errorType = mappedException.type))
    }
}.flowOn(Dispatchers.IO)

2. 修改ViewModel,收集Flow更新状态

@HiltViewModel
class ProductDetailViewModel @Inject constructor(
    state: SavedStateHandle,
    private val productRepository: ProductRepository
) : ViewModel() {

    private val passedProduct = state.get<Product>(EXTRA_KEY_DATA)
    var productId = passedProduct?.id

    var productState = mutableStateOf(ProductState())
        private set

    init {
        getProductComments()
    }

    private fun getProductComments() {
        productId?.let { pId ->
            viewModelScope.launch {
                productRepository.getProductComments(pId).collect { result ->
                    when (result) {
                        is Resource.Success -> {
                            result.data?.let { comments ->
                                productState.value = productState.value.copy(
                                    comments = comments,
                                    error = null,
                                    isLoading = false
                                )
                            }
                        }
                        is Resource.Error -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = true,
                                isLoading = false,
                                error = result.message
                            )
                        }
                        is Resource.Loading -> {
                            productState.value = productState.value.copy(
                                isLoadFailed = false,
                                isLoading = true,
                                error = null
                            )
                        }
                    }
                }
            }
        }
    }
}

3. 可选:优化Resource类(移除无用参数)

你的Loading类中的isLoading参数未被使用,可直接移除:

sealed class Resource<T>(
    val data: T? = null,
    val message: String? = null,
    val errorType: ExceptionMapper.Type? = null
) {
    class Success<T>(data: T?) : Resource<T>(data)
    class Error<T>(message: String, errorType: ExceptionMapper.Type, data: T? = null) : Resource<T>(data, message, errorType)
    class Loading<T> : Resource<T>()
}  

内容的提问来源于stack exchange,提问作者Hamed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 16:01:09