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

时间:2021-04-09 07:05:45

标签: flutter dart flutter-change-notifier

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

}

4 个答案:

答案 0 :(得分:2)

更新答案:

因此,当尝试使用 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 中如何工作的快速演示。它是从 GetX 类的 ListView.builder 列表填充的简单 ordersGetBuilder<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());
}

enter image description here

因此,如果您在此处接受 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,
            ),
          ],
        ),
      );
    });

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

那么让我们稍微简化一下。

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

  ColorChanger(this.color);

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

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:-2)

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