首页 > 解决方案 > 根据 alertdialog 选项更改卡片颜色

问题描述

我有一张卡片列表,每张卡片都有一个长按功能,点击后会弹出一个警告对话框。我希望卡片根据警报对话框中选择的选项更改颜色。我的警报对话框有 3 个选项:已完成(卡片应变为绿色)、进行中(橙色)、取消(灰色)。

首先,当屏幕加载时,它应该显示一张卡片列表,每张卡片都根据保存在数据库中的值涂上颜色。然后,当用户长按卡片并从警报对话框中选择一个选项时,卡片的颜色应根据所选选项而改变。只有该特定卡的颜色应该改变。

我在某处读到这可能可以使用 valuechangenotifier 来实现。所以这是我到目前为止所做的:

首先,我创建了我的 changenotifier 类,如下所示:

import 'package:flutter/material.dart';

class ColorChanger with ChangeNotifier{

  Color _color = Colors.white;

  ColorChanger(this._color);

  getColor() => _color;

  setTheme (Color color) {
    _color = color;
    notifyListeners();
  }

}

然后我在我的飞镖课上使用了它。但是,颜色似乎没有改变。我在这里想念什么?

class OrderItem extends StatefulWidget {
  final ord.OrderItem order;
  OrderItem(this.order);

  @override
  _OrderItemState createState() => _OrderItemState();
}

class _OrderItemState extends State<OrderItem> {
  var _expanded = false;
  var mycolor = Colors.white;

  @override
  Widget build(BuildContext context) {
    ColorChanger _color = Provider.of<ColorChanger>(context);
    var listProducts = widget.order.products;
    return Card(
      color: widget.order.orderStatus=='completed'
             ?Colors.lightGreen:widget.order.orderStatus=='inprogress'?
            Colors.orangeAccent:
             widget.order.orderStatus=='cancelled'?Colors.grey:mycolor,
      margin: EdgeInsets.all(10),
      child: Column(
        children: <Widget>[
          ListTile(
            title: RichText(
              text: new TextSpan(
                style: new TextStyle(
                  fontSize: 14.0,
                  color: Colors.black,
                ),
                children: <TextSpan>[
                  new TextSpan(
                      text: 'Order Number : ',
                      style: new TextStyle(fontWeight: FontWeight.bold)),
                  new TextSpan(text: widget.order.uniqueOrderNumber),
                ],
              ),
            ),
            trailing: IconButton(
              icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
              onPressed: () {
                setState(() {
                  _expanded = !_expanded;
                });
              },
            ),
            onLongPress: toggleSelection,
          ),
        ],
      ),
    );
  }

  void toggleSelection() {
     ColorChanger _color = Provider.of<ColorChanger>(context,listen:false);
     Widget completeOrder = FlatButton(
                    child: Text('Completed'),
                    onPressed: () async {
                      try {
                        Navigator.of(context).pop(true);
                       // setState(() {
                             _color.setTheme(Colors.lightGreen); 
                       // });
                        await Provider.of<Orders>(context, listen: false)
                            .updateOrder(widget.order,'completed');
                      } catch (error) {
                         
                      }
    });

    Widget startOrder = FlatButton(
                    child: Text('In progress'),
                    onPressed: () async {
                      try {
                        Navigator.of(context).pop(true);
                       // setState(() {
                          _color.setTheme(Colors.orangeAccent); 
                        //});
                        //Update Db to mark order in progress
                        await Provider.of<Orders>(context, listen: false)
                            .updateOrder(widget.order,'inprogress');
                      } catch (error) {
                         
                      }
    });

    Widget cancelOrder = FlatButton(
                    child: Text('Cancel'),
                    onPressed: () async {
                      try {
                        Navigator.of(context).pop(false);
                      //  setState(() {
                            _color.setTheme(Colors.grey); 
                      //  });
                        //Update Db to mark order as cancelled
                        await Provider.of<Orders>(context, listen: false)
                            .updateOrder(widget.order,'cancelled');
                      } catch (error) {
                         
                      }
    });
          showDialog(
            context: context,
            builder: (ctx) => AlertDialog(
              title: Text('Take Action'),
              content: Text('What do you want to do with the order?'),
              actions: <Widget>[
                startOrder,
                completeOrder,
                cancelOrder
              ],
            ),
          );
      });
  }
}

根据 Loren 的回答进行第二次尝试。

import 'package:flutter/material.dart';

class ColorChanger with ChangeNotifier{

  Color color = Colors.white;

  setTheme (Color newColor) {
    color = newColor;
    notifyListeners();
  }

}


class OrderItem extends StatefulWidget {
      final ord.OrderItem order;
      OrderItem(this.order);
    
      @override
      _OrderItemState createState() => _OrderItemState();
    }
    
    class _OrderItemState extends State<OrderItem> {
      var _expanded = false;
      
      
      //Set the color based on what was last saved in the DB 
      void didChangeDependencies() async {
     var colorChanger = Provider.of<ColorChanger>(context, listen: false);
     if(widget.order.orderStatus=='completed')
        colorChanger.setTheme(Colors.lightGreen);
     else if(widget.order.orderStatus=='inprogress')
        colorChanger.setTheme(Colors.orangeAccent);
      else if(widget.order.orderStatus=='cancelled')
        colorChanger.setTheme(Colors.grey);
    super.didChangeDependencies();
  }
    
      @override
      Widget build(BuildContext context) {
        var listProducts = widget.order.products;
          return  Consumer<ColorChanger>(
       builder: (context, colorChanger, child) {
        return Card(
          color: widget.order.orderStatus=='completed'
                 ?Colors.lightGreen:widget.order.orderStatus=='inprogress'?
                Colors.orangeAccent:
                 widget.order.orderStatus=='cancelled'?Colors.grey:mycolor,
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              ListTile(
                title: RichText(
                  text: new TextSpan(
                    style: new TextStyle(
                      fontSize: 14.0,
                      color: Colors.black,
                    ),
                    children: <TextSpan>[
                      new TextSpan(
                          text: 'Order Number : ',
                          style: new TextStyle(fontWeight: FontWeight.bold)),
                      new TextSpan(text: widget.order.uniqueOrderNumber),
                    ],
                  ),
                ),
                trailing: IconButton(
                  icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
                  onPressed: () {
                    setState(() {
                      _expanded = !_expanded;
                    });
                  },
                ),
                onLongPress: toggleSelection,
              ),
            ],
          ),
        )};
      }
    
      void toggleSelection() {
         ColorChanger _color = Provider.of<ColorChanger>(context,listen:false);
         Widget completeOrder = FlatButton(
                        child: Text('Completed'),
                        onPressed: () async {
                          try {
                            Navigator.of(context).pop(true);
                           // setState(() {
                                 _color.setTheme(Colors.lightGreen); 
                           // });
                            await Provider.of<Orders>(context, listen: false)
                                .updateOrder(widget.order,'completed');
                          } catch (error) {
                             
                          }
        });
    
        Widget startOrder = FlatButton(
                        child: Text('In progress'),
                        onPressed: () async {
                          try {
                            Navigator.of(context).pop(true);
                           // setState(() {
                              _color.setTheme(Colors.orangeAccent); 
                            //});
                            //Update Db to mark order in progress
                            await Provider.of<Orders>(context, listen: false)
                                .updateOrder(widget.order,'inprogress');
                          } catch (error) {
                             
                          }
        });
    
        Widget cancelOrder = FlatButton(
                        child: Text('Cancel'),
                        onPressed: () async {
                          try {
                            Navigator.of(context).pop(false);
                          //  setState(() {
                                _color.setTheme(Colors.grey); 
                          //  });
                            //Update Db to mark order as cancelled
                            await Provider.of<Orders>(context, listen: false)
                                .updateOrder(widget.order,'cancelled');
                          } catch (error) {
                             
                          }
        });
              showDialog(
                context: context,
                builder: (ctx) => AlertDialog(
                  title: Text('Take Action'),
                  content: Text('What do you want to do with the order?'),
                  actions: <Widget>[
                    startOrder,
                    completeOrder,
                    cancelOrder
                  ],
                ),
              );
          });
      }
    }

当我这样做时,它会改变所有卡片的颜色,而不仅仅是一张卡片。我在这里做错了什么?

分享order.dart

class OrderItem {
  final String id;
  final double amount;
  final int deliveryFee;
  final List<CartItem> products;
  final DateTime dateTime;
  final String deliveryMethod;
  final String uniqueOrderNumber;
  final String orderStatus; 
  final String userId;
  final String customMessage;
  final String customerName; 
  final String phoneNumber; 

  OrderItem( 
      {@required this.id,
      @required this.amount,
      @required this.products,
      @required this.dateTime,
      @required this.deliveryMethod,
      @required this.uniqueOrderNumber,
      @required this.isOrderComplete,
      this.orderStatus,
      @required this.customMessage,
      @required this.deliveryFee,
      this.customerName,
      this.phoneNumber,
      @required this.userId});
}

class Orders with ChangeNotifier {
  final String authToken;
  final String userId;

  Orders(this.authToken, this.userId);

  List<OrderItem> _orders = [];
  List<OrderItem> get orders {
    return [..._orders];
  }
  Future<void> updateOrder(OrderItem order,String orderStatus) async {
final id = order.id;
final customerId = order.userId;
final url =
    'https://cv.firebaseio.com/orders/$customerId/$id.json?auth=$authToken';
     try {
         await http.patch(url,
           body: json.encode({
             'orderStatus':orderStatus
          }));
     } catch (error) {
     print(error);
   }
notifyListeners();

}

标签: flutterdartflutter-change-notifier

解决方案


更新答案:

因此,当尝试使用 Provider 执行此操作时,我不断收到错误,这些错误需要我不断骚扰您以获得越来越多的代码来尝试复制您正在进行的所有操作,而我不想参与其中。

因此,此解决方案可能会或可能不会被您接受,因为它使用GetX State Management,但它确实有效。此外,它不需要将您的整个应用程序包装在提供程序小部件中,因此处理范围等......不是问题。

让我们为您的模型添加一个statusColor属性。OrderItem这就是将要改变的。

 Color statusColor = Colors.white; // or whatever you you want the default color to be

您使用 GetX 而不是 ChangeNotifier 的更新Orders类(同样,不是因为 Provider 不能这样做,而是因为我处理了太多错误,坦率地说 GetX 在我看来更容易)

class Orders extends GetxController {
  final String authToken;
  final String userId;

  Orders(this.authToken, this.userId);

  List<OrderItem> orders = []; // back to what I said earlier about no point in getters and setters here

// temp function just to test this on my end
  void addOrder(OrderItem order) {
    orders.add(order);
    update();
  }

// this loops through the list to find the matching order number,
// then updates the color for just that order
  void updateOrderStatusColor({OrderItem updatedOrder, String status}) {
    for (final order in orders) {
      if (order.uniqueOrderNumber == updatedOrder.uniqueOrderNumber) {
        switch (status) {
          case 'completed':
            {
              order.statusColor = Colors.greenAccent;
            }
            break;
          case 'inprogress':
            {
              order.statusColor = Colors.orangeAccent;
            }
            break;
          case 'cancelled':
            {
              order.statusColor = Colors.grey;
            }
            break;
        }
      }
    }
    update(); // equivelent of notifyListeners();
  }
  // ...the rest of your class
}

对您的卡进行一些小改动。didChangeDependencies可以完全消失。

// it seems like you had 2 classes with the same name, which is not recommended
class OrderItemCard extends StatefulWidget {
  final OrderItem order;

  OrderItemCard(this.order);

  @override
  _OrderItemCardState createState() => _OrderItemCardState();
}

class _OrderItemCardState extends State<OrderItemCard> {
  var _expanded = false;
  final controller = Get.find<Orders>(); // equivilent of Provider.of... finds the same instance without needing context

  void toggleSelection() {
    Widget completeOrder = TextButton(
        child: Text('Completed'),
        onPressed: () async {
          try {
            Navigator.of(context).pop(true);

            controller.updateOrderStatusColor(
                updatedOrder: widget.order, status: 'completed'); // calling new function here
          } catch (error) {}
        });

    Widget startOrder = FlatButton(
        child: Text('In progress'),
        onPressed: () async {
          try {
            Navigator.of(context).pop(true);
            controller.updateOrderStatusColor(
                updatedOrder: widget.order, status: 'inprogress');
          } catch (error) {}
        });

    Widget cancelOrder = FlatButton(
        child: Text('Cancel'),
        onPressed: () async {
          controller.updateOrderStatusColor(
              updatedOrder: widget.order, status: 'cancelled');
          try {
            Navigator.of(context).pop(false);
          } catch (error) {}
        });

    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text('Take Action'),
        content: Text('What do you want to do with the order?'),
        actions: <Widget>[startOrder, completeOrder, cancelOrder],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(10),
      color: widget.order.statusColor, // new color property added to your model
      child: Column(
        children: <Widget>[
          ListTile(
            title: RichText(
              text: new TextSpan(
                style: new TextStyle(
                  fontSize: 14.0,
                  color: Colors.black,
                ),
                children: <TextSpan>[
                  new TextSpan(
                      text: 'Order Number : ${widget.order.uniqueOrderNumber} ',
                      style: new TextStyle(fontWeight: FontWeight.bold)),
                ],
              ),
            ),
            trailing: IconButton(
              icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
              onPressed: () {
                setState(() {
                  _expanded = !_expanded;
                });
              },
            ),
            onLongPress: toggleSelection,
          ),
        ],
      ),
    );
  }
}

不确定你的 UI 中发生了什么,但这里有一个快速演示它如何在 GetX 中工作。它是从 GetX 类的列表中简单ListView.builder填充的。orders小部件在被调用GetBuilder<Orders>时重建。update()还有一个简单的按钮,用于添加用于演示目的的虚拟项目。我不知道您是如何生成唯一订单的 # 但我只是为此使用列表索引。两者都在演示页面的脚手架内的列内。

// Equivilent of Consumer but doesn't need context nor any provider widget above it
 GetBuilder<Orders>(
              builder: (controller) => Expanded(
                child: ListView.builder(
                    itemCount: controller.orders.length,
                    itemBuilder: (context, index) =>
                        OrderItemCard(controller.orders[index])),
              ),
            ),
            TextButton(
              onPressed: () {
                final controller = Get.find<Orders>();
                final orderItem = OrderItem(
                  orderStatus: ' ',
                  uniqueOrderNumber: controller.orders.length
                      .toString(), // just a hack to generate a unique order # for demo
                );
                controller.addOrder(orderItem);
              },
              child: Text('Add Item'),
            )

最后一件事就是初始化 GetX 控制器。只要在您尝试使用它之前,它就可以在任何地方完成。

void main() {
  // initialing the GetX GetxController
  // not sure how you're generating the required auth and user id
  // but I'm just passing in empty strings for now
  Get.put(Orders('', ''));
  runApp(MyApp());
}

在此处输入图像描述

因此,如果您在此处对 GetX 持开放态度,则可以根据需要离开 Provider 参加ChangeNotifier您可能开设的任何其他课程。为此,您只需要替换任何Consumer<Orders>GetBuilder<Order>然后完全摆脱Provider<Orders>(create:...小部件。

旧答案:

为了正确使用 Provider 并按照您想要的方式更改颜色,您错过了几件事。

对于初学者,您Card需要将其包装在一个Consumer小部件中,该小部件会收到更改通知并重建其子级。在里面Consumer,你需要使用ChangeNotifier类的颜色属性。它不需要知道或关心 ,orderStatus因为您在调用该setTheme方法时已经明确告诉它改变颜色。

Consumer<ColorChanger>(  // this is what rebuilds and changes the color
        builder: (context, colorChanger, child) {
      return Card(
        color: colorChanger.color, // colorChanger here is equivalent of declaring final colorChanger = Provider.of<ColorChanger>(context...
        child: Column(
          children: <Widget>[
            ListTile(
              title: RichText(
                text: new TextSpan(
                  style: new TextStyle(
                    fontSize: 14.0,
                    color: Colors.black,
                  ),
                  children: <TextSpan>[
                    new TextSpan(
                        text: 'Order Number : ',
                        style: new TextStyle(fontWeight: FontWeight.bold)),
                    new TextSpan(text: widget.order.uniqueOrderNumber),
                  ],
                ),
              ),
              trailing: IconButton(
                icon: Icon(_expanded ? Icons.expand_less : Icons.expand_more),
                onPressed: () {
                  setState(() {
                    _expanded = !_expanded;
                  });
                },
              ),
              onLongPress: toggleSelection,
            ),
          ],
        ),
      );
    });

接下来,请参阅此链接,了解为什么在课堂上使用私有_color和公共没有任何收获。getColorChangeNotifier

所以让我们稍微简化一下。

class ColorChanger with ChangeNotifier {
  Color color = Colors.white;

  ColorChanger(this.color);

  setTheme(Color newColor) {
    color = newColor;
    notifyListeners();
  }
}

现在,每当您setTheme从对话框中调用该函数时,该卡将更改为您传递给它的任何颜色,因为Consumer小部件会收到通知,并将使用ChangeNotifier类的更新颜色值进行重建。


推荐阅读