如何仅显示最新的添加购物车操作对应的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
相关产品推荐
相关产品推荐

