Flutter Provider无法实时更新购物车总价按钮显示求助
问题描述
开发了一个Provider用于计算多个Food类实例的总价并展示在按钮上,但按钮的总价只有在离开页面返回或热重载后才会更新。userOrders列表存储购物车中所有添加的商品,计划通过fold函数计算每个商品单价与数量的乘积之和,并将结果显示在按钮上。
现有代码
Food类
class Food { String imgUrl; String desc; String name; String waitTime; num score; int price; int pq; int quantity; bool favourited; List<Map<String, String>> ingredients; String about; bool highlight; Food(this.imgUrl, this.desc, this.name, this.pq, this.waitTime, this.score, this.price, this.quantity, this.ingredients, this.about, this.favourited, {this.highlight = false}); int pqProduct(Food food) { pq = food.quantity * food.price; return pq; } }
Provider类
class FavFood with ChangeNotifier { int peeQee() { int pqe = userOrders.fold(0, (a, element) => a + element.pqProduct(element)); return pqe; } void update() { notifyListeners(); } }
按钮组件
class NextButton extends StatefulWidget { @override State<NextButton> createState() => _NextButtonState(); } class _NextButtonState extends State<NextButton> { @override Widget build(BuildContext context) { return userOrders.isNotEmpty ? Container( color: Colors.transparent, padding: const EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 5.0), height: 60, width: double.infinity, child: ElevatedButton( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(18.0), side: const BorderSide(color: Colors.transparent), ), ), ), child: Text( '${context.read<FavFood>().peeQee}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18.0, ), ), onPressed: () {}, ), ) : const Text(''); } }
Food类实例示例
Food( 'assets/images/sha-modified.png', 'Most Recommended', 'Shawarma', 0, '15 min', 4.5, 1500, 0, [ { 'Flatbread': 'assets/images/flatbread-modified.png', }, {'Cabbage': 'assets/images/cabbb-modified.png'}, {'Chicken': 'assets/images/chicken-modified.png'}, {'Sauce': 'assets/images/sauce-modified.png'}, ], 'A sandwich of sliced lamb or chicken, vegetables, and often tahini wrapped in pita bread', highlight: true, false, ),
问题原因分析
- 组件未监听Provider变化:按钮使用
context.read<FavFood>(),该方法仅获取Provider实例但不监听状态变更,因此notifyListeners()触发时不会重建组件。 - 状态变更未自动通知:
userOrders为全局变量,添加/修改商品时未主动调用Provider的notifyListeners(),无法触发UI更新。 - 不必要的实例属性修改:
pqProduct方法直接修改Food实例的pq属性,既冗余又可能导致状态混乱。 - 全局状态管理失控:
userOrders脱离Provider管理,无法统一控制状态变更的通知逻辑。
解决方案
1. 优化Food类
移除冗余的pq属性和pqProduct方法,直接通过计算属性获取单商品总价:
class Food { String imgUrl; String desc; String name; String waitTime; num score; int price; int quantity; bool favourited; List<Map<String, String>> ingredients; String about; bool highlight; Food( this.imgUrl, this.desc, this.name, this.waitTime, this.score, this.price, this.quantity, this.ingredients, this.about, this.favourited, { this.highlight = false, }); // 计算单商品总价,不修改实例属性 int get totalPrice => price * quantity; }
2. 重构Provider类
将userOrders移至Provider内部管理,添加商品操作方法并自动触发状态通知:
class FavFood with ChangeNotifier { final List<Food> _userOrders = []; // 返回不可变副本,避免外部直接修改 List<Food> get userOrders => [..._userOrders]; // 计算购物车总价 int get totalPrice => _userOrders.fold(0, (sum, food) => sum + food.totalPrice); // 添加商品(存在则增加数量,不存在则新增) void addFood(Food food) { final existingItem = _userOrders.firstWhere( (item) => item.name == food.name, // 可根据实际调整唯一标识 orElse: () => Food('', '', '', '', 0, 0, 0, [], '', false), ); if (existingItem.name.isNotEmpty) { existingItem.quantity += 1; } else { _userOrders.add(Food( food.imgUrl, food.desc, food.name, food.waitTime, food.score, food.price, 1, // 默认添加数量为1 food.ingredients, food.about, food.favourited, highlight: food.highlight, )); } notifyListeners(); // 触发UI更新 } // 修改商品数量(数量≤0则移除) void updateQuantity(Food food, int newQuantity) { final existingItem = _userOrders.firstWhere( (item) => item.name == food.name, orElse: () => Food('', '', '', '', 0, 0, 0, [], '', false), ); if (existingItem.name.isNotEmpty) { existingItem.quantity = newQuantity; if (existingItem.quantity <= 0) _userOrders.remove(existingItem); notifyListeners(); // 触发UI更新 } } }
3. 修改按钮组件
使用context.watch<FavFood>()监听状态变化,改为无状态组件简化代码:
class NextButton extends StatelessWidget { @override Widget build(BuildContext context) { final favFood = context.watch<FavFood>(); return favFood.userOrders.isNotEmpty ? Container( color: Colors.transparent, padding: const EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 5.0), height: 60, width: double.infinity, child: ElevatedButton( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(18.0), side: const BorderSide(color: Colors.transparent), ), ), ), child: Text( '${favFood.totalPrice}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18.0, ), ), onPressed: () {}, ), ) : const Text(''); } }
4. 更新Food实例示例
移除冗余的pq参数:
Food( 'assets/images/sha-modified.png', 'Most Recommended', 'Shawarma', '15 min', 4.5, 1500, 0, [ {'Flatbread': 'assets/images/flatbread-modified.png'}, {'Cabbage': 'assets/images/cabbb-modified.png'}, {'Chicken': 'assets/images/chicken-modified.png'}, {'Sauce': 'assets/images/sauce-modified.png'}, ], 'A sandwich of sliced lamb or chicken, vegetables, and often tahini wrapped in pita bread', false, highlight: true, ),
内容的提问来源于stack exchange,提问作者shimomaru
相关产品推荐
相关产品推荐

