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

Flutter:使用Provider实现商品数量达10时仅触发一次Snackbar的问题

Flutter商品数量选择器SnackBar重复弹出问题修复

问题描述

实现商品数量选择器时遇到以下问题:

  • 需求:数量到10后点击「添加」按钮不再改变数量,且仅弹出一次Snackbar,之后不再弹出
  • 当前问题:数量达到10后,每次点击「添加」仍会重复弹出Snackbar

现有代码

Provider状态管理类

class QuantitySelector extends ChangeNotifier {
  final Map<int, int> _quantities = {}; 

  int getQuantityValue(int productId) =>
      _quantities[productId] ??
      1;

  snackBar(context, int productId) {
    final currentValue = _quantities[productId] ?? 1;
    if (currentValue <= 10) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          elevation: 0.0,
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
          backgroundColor: const Color(0xFF6DBD28),
          content: Row(
            children: [
              const Icon(
                Icons.done,
                color: Colors.white,
              ),
              buildSizeBoxWidth(10),
              Text(
                AppStrings.kSnackBarText,
                style: AppStyles.kSnackBarTextStyles,
              ),
            ],
          ),
          behavior: SnackBarBehavior.floating,
          dismissDirection: DismissDirection.none,
          margin: EdgeInsets.only(
            bottom: MediaQuery.of(context).size.height - 160,
          ),
        ),
      );
    }
  }

  void quantityIncrement(int productId) {
    final currentValue = _quantities[productId] ?? 1;
    if (currentValue < 10) {
      _quantities[productId] = currentValue + 1;
      notifyListeners();
    }
  } 

  void quantityDecrement(int productId) {
    final currentValue = _quantities[productId] ?? 1;
    if (currentValue > 1) {
      _quantities[productId] = currentValue - 1;
      notifyListeners();
    }
  } 

  void resetProductQuantity(int productId) {
    _quantities.remove(productId);
    notifyListeners();
  }

  Color quantityIncrementButtonColor(int productId) {
    final currentValue = _quantities[productId] ?? 1;
    if (currentValue < 10) {
      return AppColors.kPrimaryColor;
    }
    return AppColors.kGrayColor.withOpacity(0.5);
  }

  Color quantityDecrementButtonColor(int productId) {
    final currentValue = _quantities[productId] ?? 1;
    if (currentValue > 1) {
      return AppColors.kPrimaryColor;
    }
    return AppColors.kGrayColor.withOpacity(0.5);
  }
}

数量选择器Widget

class ProductQuantitySelectorRow extends StatelessWidget {
  final int productId;
  const ProductQuantitySelectorRow({super.key, required this.productId});

  @override
  Widget build(BuildContext context) {
    final quantitySelector = Provider.of<QuantitySelector>(context);
    return Row(
      children: [
        ProductQuantitySelector(
          color: quantitySelector.quantityDecrementButtonColor(productId),
          margin: const EdgeInsets.only(right: 10),
          symbol: '-',
          onTap: () {
            quantitySelector.quantityDecrement(productId);
            // showCustom(context);
          },
        ),
        SizedBox(
          height: 30,
          width: 20,
          child: Center(
            child: Text(
              quantitySelector.getQuantityValue(productId).toString(),
            ),
          ),
        ),
        ProductQuantitySelector(
          margin: const EdgeInsets.only(left: 10),
          symbol: '+',
          color: quantitySelector.quantityIncrementButtonColor(productId),
          onTap: () {
            quantitySelector.quantityIncrement(productId);
           quantitySelector.snackBar(context,productId);
          },
        )
      ],
    );
  }
}

问题根源

  1. snackBar方法的判断条件currentValue <=10过于宽泛,当数量已经是10时,每次点击都会触发弹出
  2. Widget中每次点击「添加」都会无条件调用snackBar方法,没有控制触发时机

修复方案

步骤1:添加状态标记控制Snackbar弹出次数

在QuantitySelector类中新增一个Map,记录每个商品是否已经显示过数量上限的Snackbar:

final Map<int, bool> _hasShownMaxSnackbar = {};

步骤2:整合Snackbar触发逻辑到增量方法

将Snackbar的显示逻辑整合到quantityIncrement中,控制仅在数量变化或首次达到上限时弹出:

// 重命名原snackBar方法为私有方法,避免外部随意调用
void _showSnackbar(BuildContext context, int productId) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      elevation: 0.0,
      shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
      backgroundColor: const Color(0xFF6DBD28),
      content: Row(
        children: [
          const Icon(
            Icons.done,
            color: Colors.white,
          ),
          buildSizeBoxWidth(10),
          Text(
            AppStrings.kSnackBarText,
            style: AppStyles.kSnackBarTextStyles,
          ),
        ],
      ),
      behavior: SnackBarBehavior.floating,
      dismissDirection: DismissDirection.none,
      margin: EdgeInsets.only(
        bottom: MediaQuery.of(context).size.height - 160,
      ),
    ),
  );
}

void quantityIncrement(int productId, BuildContext context) {
  final currentValue = _quantities[productId] ?? 1;
  if (currentValue < 10) {
    _quantities[productId] = currentValue + 1;
    notifyListeners();
    _showSnackbar(context, productId);
  } else if (currentValue == 10 && !(_hasShownMaxSnackbar[productId] ?? false)) {
    // 仅首次达到10时弹出Snackbar
    _showSnackbar(context, productId);
    _hasShownMaxSnackbar[productId] = true;
  }
}

步骤3:修改Widget的点击逻辑

移除Widget中单独调用snackBar的代码,改为直接调用整合后的增量方法:

onTap: () {
  quantitySelector.quantityIncrement(productId, context);
},

可选:重置状态时清除标记

如果需要支持重置商品数量,在resetProductQuantity方法中同时清除对应的Snackbar标记:

void resetProductQuantity(int productId) {
  _quantities.remove(productId);
  _hasShownMaxSnackbar.remove(productId); // 清除标记
  notifyListeners();
}

修复后效果

  • 数量小于10时,每次点击「添加」都会增加数量并弹出Snackbar
  • 数量从9增加到10时,弹出一次Snackbar并标记,后续点击不再弹出
  • 数量已达10且已标记后,点击「添加」无任何操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:05:54