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

Flutter Provider异常:添加商品不触发UI更新,删除时刷新正常

问题原因
  • 你在build方法中获取AllGoodsViewModel实例时写了listen: false,这会直接让当前页面放弃监听ViewModel的notifyListeners()通知,ViewModel里的属性修改后,不会主动触发页面重建。
  • 删除操作看着能正常刷新,根本不是Provider通知生效,是你调用changeButtonState的代码被包在了setState()里面,setState会强制页面重新执行build,自然会读到最新的isEmpty值,所以状态切换看起来正常。
  • 添加商品、增减商品数量走的是haveItem方法,这个方法里调用changeButtonState的时候没有包裹setState,本来指望notifyListeners触发UI更新,但是之前已经关闭了监听,页面根本收不到更新信号,当然不会刷新按钮状态。
  • 额外隐患:你现在的状态逻辑非常混乱,既在ViewModel里改状态,又在UI层的setState里直接修改购物车数据、操作本地数据库,状态来源不统一,就算这次问题修好了,后续也很容易出现其他UI和数据不同步的问题。
修复方法
  1. 打开Provider的监听,两种实现选其一即可:要么把获取model代码里的listen: false改成listen: true,更推荐用Consumer只包裹底部需要刷新的按钮区域,不用整页跟着重建,性能更好。
  2. 把所有修改状态、操作数据的逻辑全部收拢到ViewModel中,UI层只负责点击按钮时调用VM的对应方法、根据VM返回的状态渲染界面,不要在UI层的setState里直接改购物车数据、操作数据库,保证状态只有VM这一个修改入口。

核心代码修正示例

ViewModel层调整

把所有购物车操作逻辑移到ViewModel内,UI层不再直接处理数据:

class AllGoodsViewModel extends ChangeNotifier {
  var _isEmpty = true;
  bool get isEmpty => _isEmpty;

  void detailSetting(Goods item) {
    final ind = cart.value.indexWhere((element) => element.id == item.id);
    _isEmpty = ind == -1;
    notifyListeners();
  }

  void changeButtonState(bool state) {
    _isEmpty = state;
    notifyListeners();
  }

  Future<void> haveItem({required Goods item, required int operation}) async {
    final ind = cart.value.indexWhere((element) => element.id == item.id);
    if (ind == -1) {
      final minCount = item.optState == 0 ? 1 : item.opt!.count;
      if (item.count < minCount) return;
      
      changeButtonState(false);
      cart.value.add(item);
      final newInd = cart.value.length - 1;
      cart.value[newInd].isOpt = item.optState == 0 ? false : true;
      cart.value[newInd].orderCount = minCount;
      cart.value = List.from(cart.value);
      await SQFliteService.cart.addToCart(cart.value[newInd]);
      changeCountInCart(operation);
    }  else {
      final count = cart.value[ind].orderCount;
      if (count > item.count) return;
      if (operation >=0 && count + operation > item.count) return;
      
      // 减到最低阈值时直接切换为空状态
      if (operation < 0 && (count + operation) <= (item.optState == 0 ? 1 : item.opt!.count)) {
        changeButtonState(true);
        cart.value[ind].orderCount = 0;
        await SQFliteService.cart.delete(cart.value[ind].id);
        cart.value = List.from(cart.value)..removeAt(ind);
        changeCountInCart(operation);
        return;
      }
      changeButtonState(false);
      cart.value[ind].orderCount += operation;
      cart.value = List.from(cart.value);
      await SQFliteService.cart.updateItem(cart.value[ind].id, {"orderCount":cart.value[ind].orderCount});
      changeCountInCart(operation);
    }
  }
}

UI层调整

去掉UI层直接操作数据、setState包裹VM调用的逻辑,用Consumer监听状态刷新:

@override
Widget build(BuildContext context) {
  return ScreenUtilInitService().build((context) => Scaffold(
    backgroundColor: Colors.white,
    body: Container(
      height: 64.h,
      color: Colors.white,
      // 仅监听包裹范围内的组件刷新
      child: Consumer<AllGoodsViewModel>(
        builder: (context, model, child) {
          return model.isEmpty ? noInCart(model) : inCart(model);
        },
      ),
    ),
  ));
}

Widget inCart(AllGoodsViewModel model){
  return GestureDetector(
    onPanDown: (details) async {
      Goods item = widget.item;
      RenderBox _cardBox = _key.currentContext!.findRenderObject() as RenderBox;
      final localDx = details.localPosition.dx;
      if (localDx <= _cardBox.size.width/2) {
        Goods value = cart.value.firstWhere((element) => element.id == item.id);
        // 所有操作统一走VM方法,UI层不直接处理数据
        await model.haveItem(
          item: item, 
          operation: item.optState == 0 ? -1 : (-1 * value.opt!.count)
        );
      } else {
        await model.haveItem(
          item: item, 
          operation: item.optState == 0 ? 1 : item.count
        );
      }
    },
    child: /* 原inCart组件内容 */
  );
}

Widget noInCart(AllGoodsViewModel model){
  return Container(
    width: size.width - 16.w,
    margin: EdgeInsets.symmetric(vertical: 10.h),
    key: _key,
    child: TextButton(
      style: /* 原按钮样式 */,
      onPressed: (){
        model.haveItem(item: widget.item, operation: widget.item.optState == 0 ? 1 : widget.item.count);
      },
      child: /* 原noInCart组件内容 */
    ),
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:39:42