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

如何仅显示最新的添加购物车操作对应的Snackbar?

解决方案

问题根源

你当前的防抖逻辑是延迟执行API请求,这会导致:

  • 若用户在防抖间隔内连续点击不同商品,只会触发最后一次请求,仅显示一次Snackbar(符合预期)
  • 若用户点击间隔超过防抖时间,每个请求都会执行,每个成功/失败结果都会弹出Snackbar(不符合预期)

而你需要的是无论用户点击多少次,最终只显示最后一次操作的Snackbar,而非阻止前面的API请求(毕竟用户点击的多个商品都需要正常添加到购物车)。

两种可行方案

方案1:立即执行所有API请求,仅防抖显示最后一次Snackbar

该方案会正常处理所有加购请求,仅在用户停止操作后,显示最后一次操作的结果:

Timer? debounceSnackBarTimer;
// 保存最后一次操作的Snackbar内容和样式信息
Map<String, dynamic>? lastSnackbarInfo;

Future<void> addToCart(Package package) async {
  final prefs = await SharedPreferences.getInstance();
  final accessToken = prefs.getString('accessToken');
  final String apiUrl = await AppNetworkingUrls.addtoCart;

  if (accessToken == null) {
    lastSnackbarInfo = {
      'message': 'Please log in to add items to the cart',
      'backgroundColor': Colors.grey,
      'style': GoogleFonts.poppins(),
    };
    triggerDebouncedSnackbar();
    return;
  }

  final url = Uri.parse(apiUrl);
  final headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer $accessToken',
  };

  final body = jsonEncode({
    'quantity': 1,
    'productId': product.id,
    'packageId': package.id,
  });

  try {
    final response = await http.post(url, headers: headers, body: body);
    final responseData = jsonDecode(response.body);

    if (response.statusCode == 200) {
      lastSnackbarInfo = {
        'message': responseData['message'] ?? 'Item added to cart',
        'backgroundColor': AppColors.buttonColor,
        'style': GoogleFonts.poppins(fontWeight: FontWeight.w500),
      };
      final cartProvider = Provider.of<Cart>(context, listen: false);
      await cartProvider.fetchCartItems();
    } else {
      lastSnackbarInfo = {
        'message': responseData['message'] ?? 'Failed to add item',
        'backgroundColor': Colors.red,
        'style': GoogleFonts.poppins(fontWeight: FontWeight.w500),
      };
    }
  } catch (error) {
    print('Error adding to cart: $error');
    lastSnackbarInfo = {
      'message': 'Something went wrong. Please try again.',
      'backgroundColor': Colors.red,
      'style': GoogleFonts.poppins(fontWeight: FontWeight.w500),
    };
  }

  // 触发防抖显示Snackbar
  triggerDebouncedSnackbar();
}

void triggerDebouncedSnackbar() {
  debounceSnackBarTimer?.cancel();
  debounceSnackBarTimer = Timer(const Duration(milliseconds: 800), () {
    if (lastSnackbarInfo != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          behavior: SnackBarBehavior.floating,
          backgroundColor: lastSnackbarInfo!['backgroundColor'],
          duration: const Duration(seconds: 2),
          content: Text(
            lastSnackbarInfo!['message'],
            style: lastSnackbarInfo!['style'],
          ),
        ),
      );
    }
  });
}

方案2:显示新Snackbar时自动替换旧的

该方案更简单,每次显示Snackbar前清除所有已存在的Snackbar,确保始终只显示最新的提示:

Future<void> addToCart(Package package) async {
  final prefs = await SharedPreferences.getInstance();
  final accessToken = prefs.getString('accessToken');
  final String apiUrl = await AppNetworkingUrls.addtoCart;

  if (accessToken == null) {
    // 清除旧Snackbar后显示新的
    ScaffoldMessenger.of(context).clearSnackBars();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(
          'Please log in to add items to the cart',
          style: GoogleFonts.poppins(),
        ),
      ),
    );
    return;
  }

  final url = Uri.parse(apiUrl);
  final headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer $accessToken',
  };

  final body = jsonEncode({
    'quantity': 1,
    'productId': product.id,
    'packageId': package.id,
  });

  try {
    final response = await http.post(url, headers: headers, body: body);
    final responseData = jsonDecode(response.body);

    if (response.statusCode == 200) {
      print(response.body);
      // 先清除旧的,再显示新的
      ScaffoldMessenger.of(context).clearSnackBars();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          behavior: SnackBarBehavior.floating,
          backgroundColor: AppColors.buttonColor,
          duration: const Duration(seconds: 2),
          content: Text(
            responseData['message'] ?? 'Item added to cart',
            style: GoogleFonts.poppins(fontWeight: FontWeight.w500),
          ),
        ),
      );

      final cartProvider = Provider.of<Cart>(context, listen: false);
      await cartProvider.fetchCartItems();
    } else {
      ScaffoldMessenger.of(context).clearSnackBars();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          behavior: SnackBarBehavior.floating,
          backgroundColor: Colors.red,
          duration: const Duration(seconds: 2),
          content: Text(
            responseData['message'] ?? 'Failed to add item',
            style: GoogleFonts.poppins(fontWeight: FontWeight.w500),
          ),
        ),
      );
    }
  } catch (error) {
    print('Error adding to cart: $error');
    ScaffoldMessenger.of(context).clearSnackBars();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        behavior: SnackBarBehavior.floating,
        backgroundColor: Colors.red,
        duration: const Duration(seconds: 2),
        content: Text(
          'Something went wrong. Please try again.',
          style: GoogleFonts.poppins(fontWeight: FontWeight.w500),
        ),
      ),
    );
  }
}

方案选择

  • 方案1:适合希望用户停止操作后再统一提示的场景,避免频繁弹出Snackbar打扰用户
  • 方案2:适合需要即时反馈,但确保最新操作提示覆盖旧提示的场景,实现更简单

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:05:02