根据警报对话框选项更改卡片颜色

Change card color based on alertdialog option

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

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

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

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

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。但是,颜色似乎没有变化。我在这里错过了什么?

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();

}

像这样的事情将是您想要实现的最简单的方法:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  // define a list of colors:
  final colors = <Color>[
    Colors.white, // this is the inital color
    Colors.green,
    Colors.orange,
    Colors.grey
  ];
  int index = 0;

  Future<int> showMyDialog(BuildContext context) async {
    // Since all Navigator.push(...) and showDialog(...) calls are futures
    // we can send values alongside them when we pop the context:
    // final value = await Navigator.push(...);
    // or
    // final value = await showDialog(...);
    // then we do a:
    // Navigator.pop(context, SOME_VALUE,);
    // the value variable will be assigned to the one we sent
    return await showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Take Action'),
        content: Text('What do you want to do with the order?'),
        actions: <Widget>[
          TextButton(
              child: Text('Completed',
                  style: TextStyle(
                    color: Colors.green,
                  )),
              onPressed: () => Navigator.pop(context, 1)),
          TextButton(
              child: Text('In progress',
                  style: TextStyle(
                    color: Colors.orange,
                  )),
              onPressed: () => Navigator.pop(context, 2)),
          TextButton(
              child: Text('Cancel',
                  style: TextStyle(
                    color: Colors.grey,
                  )),
              onPressed: () => Navigator.pop(context, 3)),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(children: <Widget>[
        Card(
          color: colors[index],
          child: Container(width: 50, height: 50),
        ),
        ElevatedButton(
            child: Text('Show dialog'),
            onPressed: () async {
              // call the showMyDialog function, it returns
              // a future int so we have to await it
              final int _index = await showMyDialog(context);
              
              // if the returned value (_index) is null we use
              // the old one value to avoid erros in the code
              setState(() => index = _index ?? index);
            }),
      ]),
    );
  }
}

更新的答案:

因此,当尝试使用 Provider 执行此操作时,我不断收到错误,这需要我不断地窃听您的代码以尝试复制您正在进行的所有操作,但我不想进入那。

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

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

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

您更新的 Orders class 使用 GetX 而不是 ChangeNotifier(同样,不是因为 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 Class 的 orders 列表中填充的简单 ListView.builderGetBuilder<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 classes,如果您愿意的话。为此,您只需将任何 Consumer<Orders> 替换为 GetBuilder<Order>,然后完全删除 Provider<Orders>(create:... 小部件。

旧答案:

为了正确使用 Provider 并按照您想要的方式更改颜色,您缺少一些东西。

对于初学者,您的 Card 需要包装在一个 Consumer 小部件中,该小部件会收到更改通知并重建其子项。在 Consumer 内,您需要使用 ChangeNotifier class 的颜色 属性。它不需要知道或关心 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,
            ),
          ],
        ),
      );
    });

接下来,see this link 关于为什么在 ChangeNotifier class.

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

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

  ColorChanger(this.color);

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

现在,无论何时您从对话框中调用 setTheme 函数,该卡片都会更改为您传递给它的任何颜色,因为 Consumer 小部件已收到通知,并且会使用更新后的颜色重建ChangeNotifier class.

的值

最好的实现方式是使用AwesomeDialog https://pub.dev/packages/awesome_dialog

AwesomeDialog(
            context: context,
            dialogType: DialogType.INFO,
            animType: AnimType.BOTTOMSLIDE,
            title: 'Dialog Title',
            desc: 'Dialog description here.............',
            btnCancelOnPress: () {},
            btnOkOnPress: () {},
            )..show();

一个非常简单的解决方法是声明一个全局颜色变量 cardColor 并将其分配给卡片的颜色 属性。然后在 alertdialog 上,更改小部件的“onChange”或 'onTap' 属性,以便在点击时,小部件将全局变量 cardColor 的值更改为不同的颜色。不要忘记执行最后一步,即在 setState()

中更改变量的值