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

Android Jetpack Compose:API响应成功后更新商品购物车计数

解决方案:等待API响应成功后更新购物车计数

核心思路

  1. 集中管理商品请求状态:ViewModel中用Map跟踪每个商品变体的API请求状态,避免多个商品请求互相覆盖。
  2. 状态提升与单向数据流:商品列表状态由父组件管理,子组件通过回调更新状态,避免直接修改本地数据。
  3. 监听API响应更新UI:在商品Item中监听对应商品的请求状态,仅当响应成功且Result == false时更新计数。

1. 修改ViewModel,跟踪每个商品的请求状态

原ViewModel仅维护单个请求结果,多商品请求会互相覆盖,改用Map存储每个商品变体ID对应的请求状态:

@HiltViewModel
class AddProductVM @Inject constructor(
    private val apiService: ApiService,
    private val defaultDispatcher: CoroutineDispatcher
): ViewModel() {
    // 用商品变体ID作为Key,存储每个商品的请求状态
    private val _addProductStates = MutableStateFlow<Map<String, ApiResult<AddProduct>>>(emptyMap())
    val addProductStates = _addProductStates.asStateFlow()

    fun addProduct(productId: String, qty: String, productVariantId: String){
        // 更新当前商品的状态为加载中
        _addProductStates.update { currentMap ->
            currentMap + (productVariantId to ApiResult.Loading())
        }
        viewModelScope.launch {
            apiService.addProduct(productId, qty, productVariantId)
                .flowOn(defaultDispatcher)
                .catch {
                    // 请求失败,更新状态
                    _addProductStates.update { currentMap ->
                        currentMap + (productVariantId to ApiResult.Error(it.message ?: "Something went wrong"))
                    }
                }
                .collect{ result ->
                    // 请求完成,更新状态
                    _addProductStates.update { currentMap ->
                        currentMap + (productVariantId to result)
                    }
                }
        }
    }
}

2. 修改ProductItem组件,监听响应并更新计数

将商品状态的修改权限交给父组件,同时监听当前商品的API请求状态,处理加载、成功、失败逻辑:

@Composable
fun ProductItem(
    product: Product,
    onUpdateProductCount: (variantId: String, newCount: Int) -> Unit,
    addProductViewModel: AddProductVM = hiltViewModel()
) {
    val addProductStates by addProductViewModel.addProductStates.collectAsState()
    val currentVariant = product.variants[0]
    val currentRequestState = addProductStates[currentVariant.id]

    // 监听请求状态变化,仅当成功且Result==false时更新计数
    LaunchedEffect(currentRequestState) {
        if (currentRequestState is ApiResult.Success) {
            val isApiError = currentRequestState.data?.error ?: true
            if (!isApiError) {
                val newCount = currentVariant.cart_count + 1
                onUpdateProductCount(currentVariant.id, newCount)
            }
        }
    }

    Card(modifier = Modifier.fillMaxWidth()) {
        Row(modifier = Modifier.fillMaxHeight()) {
            Column {
                Row {
                    Box(
                        modifier = Modifier.weight(1f),
                        contentAlignment = Alignment.Center
                    ) {
                        when(currentRequestState) {
                            // 加载中显示进度条
                            is ApiResult.Loading -> CircularProgressIndicator(modifier = Modifier.size(20.dp))
                            else -> {
                                if (currentVariant.cart_count > 0) {
                                    Text(text = currentVariant.cart_count.toString())
                                } else {
                                    Text(
                                        text = "Add To Cart",
                                        modifier = Modifier.clickable {
                                            val newCount = currentVariant.cart_count + 1
                                            addProductViewModel.addProduct(
                                                currentVariant.product_id.toString(),
                                                newCount.toString(),
                                                currentVariant.id.toString()
                                            )
                                        }
                                    )
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

3. 父组件管理商品列表状态

在商品列表组件中维护可变的商品列表,通过回调更新对应商品的计数:

@Composable
fun ProductList(initialProducts: List<Product>) {
    // 用mutableStateListOf维护可更新的商品列表
    val mutableProducts = remember { mutableStateListOf(*initialProducts.toTypedArray()) }

    LazyColumn {
        items(mutableProducts) { product ->
            ProductItem(
                product = product,
                onUpdateProductCount = { variantId, newCount ->
                    // 找到对应商品并更新计数
                    val targetIndex = mutableProducts.indexOfFirst { it.variants[0].id == variantId }
                    if (targetIndex != -1) {
                        val updatedProduct = product.copy(
                            variants = product.variants.map {
                                if (it.id == variantId) it.copy(cart_count = newCount) else it
                            }
                        )
                        mutableProducts[targetIndex] = updatedProduct
                    }
                }
            )
        }
    }
}

4. 优化全局响应处理(可选)

如果需要全局处理API响应的日志或提示,修改AddProductResult组件:

@Composable
fun AddProductResult(addProductStates: Map<String, ApiResult<AddProduct>>) {
    addProductStates.values.forEach { state ->
        when (state) {
            is ApiResult.Error -> Log.e("addproductapicall", "请求失败: ${state.error}")
            is ApiResult.Success -> {
                val isError = state.data?.error ?: true
                if (isError) {
                    Log.e("addproductapicall", "业务错误: ${state.data?.message}")
                } else {
                    Log.d("addproductapicall", "请求成功: ${state.data?.message}")
                }
            }
            else -> {}
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:43:14