根据 alertdialog 选项更改卡片颜色

Posted

技术标签:

【中文标题】根据 alertdialog 选项更改卡片颜色【英文标题】:Change card color based on alertdialog option 【发布时间】:2021-07-05 01:31:51 【问题描述】:

我有一张卡片列表,每张卡片都有一个长按功能,点击后会弹出一个警告对话框。我希望卡片根据警报对话框中选择的选项更改颜色。我的警报对话框有 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();

【问题讨论】:

【参考方案1】:

更新答案:

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

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

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

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

您更新后的Orders 类使用 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 中工作。这是一个简单的ListView.builder,由GetX 类的orders 列表填充。 GetBuilder&lt;Orders&gt; 小部件在调用 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&lt;Orders&gt; 替换为 GetBuilder&lt;Order&gt;,然后完全摆脱 Provider&lt;Orders&gt;(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,
            ),
          ],
        ),
      );
    );

接下来,see this link 解释为什么在 ChangeNotifier 类中使用私有 _color 和公共 getColor 没有任何收获。

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

class ColorChanger with ChangeNotifier 
  Color color = Colors.white;

  ColorChanger(this.color);

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

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

【讨论】:

请根据您的建议查看我编辑的答案。 我没有意识到你有多张卡片。你有几张卡?还是会改变? 根据订单数量而变化。每个订单都驻留在自己的卡片中,并且每个订单都与订单状态相关联。所以我只想更改特定卡的订单状态。 明白了,抱歉,也许我应该假设不止一个。今天没有时间看这个,但我会在第二天或第二天回来查看,如果您还没有找到解决方案,我会帮助您。 谢谢洛伦。这两天我会搜索更多。如果我不能解决它,我会伸出援手【参考方案2】:

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

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);
            ),
      ]),
    );
  

【讨论】:

【参考方案3】:

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

【讨论】:

这将如何改变卡片本身的颜色? 当您在 setState() 中更改 Flutter 中变量 (cardColor) 的值时,flutter 会更改所有依赖于该变量的可见 UI 元素。因为卡片的 color 属性设置为变量 cardColor,调用 setstate 会改变卡片的活动颜色。【参考方案4】:

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

【讨论】:

这如何改变卡片的颜色? 您可以使用 btnCancelOnPress 或 btnOkOnPress 更改卡片的颜色。

以上是关于根据 alertdialog 选项更改卡片颜色的主要内容,如果未能解决你的问题,请参考以下文章

AlertDialog 样式 - 如何更改标题、消息等的样式(颜色)

不是我创建的 Android 默认 alertdialog 背景颜色

如何在 Flutter 中选择项目时更改卡片颜色?

如何在卡片内的按钮上进行 onclick 事件:更改卡片背景颜色、按钮背景和文本颜色以及文本内容

如何在Flutter中刷新AlertDialog?

如何动态更改 AlertDialog 的布局和字段