根据 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<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,
),
],
),
);
);
接下来,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 背景颜色