Flutter中如何基于数组数据动态修改Container颜色?
问题根源
你的核心问题是数据更新顺序错误,导致oldData和newData始终持有相同的数据:
每次收到WebSocket消息时,你先更新newData为最新数据,再把newData的内容复制给oldData,这就使得UI渲染时新旧数据完全一致,自然无法触发颜色变化逻辑。
另外还有两个次要问题:
- 颜色变量
back如果是类成员变量,会在ListView.builder循环中被覆盖,导致所有Item颜色统一 - 空安全处理不够严谨,存在潜在空指针风险
修复方案
1. 修正数据更新逻辑
调整WebSocket回调中的数据处理顺序,先备份旧数据,再更新新数据:
void getSocket(){ ioChannel = IOWebSocketChannel.connect( Uri.parse('wss://xxx.xyz/port=${widget.port}/?logged=true&source=0'), headers: {'Origin': 'https://abc.io'}, ); ioChannel?.stream.listen((message) { final data = jsonDecode(message); if (data[0] != null) { final match = SocketModel.fromJson(data[0]); // 步骤1:先把当前newData的内容备份到oldData(创建副本,避免引用关联) setState(() { oldData = List.from(newData!); }); // 步骤2:更新newData为最新数据 newData!.clear(); for (var item in match.runners!) { var obj = CustomRunners( marketId: match.marketId, runnerName: item.runnerName, selectionId: item.selectionId, status: item.status, exchangePrices: item.exchangePrices, lastPriceTraded: item.lastPriceTraded, totalMatched: item.totalMatched); newData?.add(obj); } } }); }
2. 修复颜色变量作用域
在ListView.builder的itemBuilder内部声明back变量,避免循环覆盖:
SizedBox( height: 150, width: MediaQuery.of(context).size.width, child: ListView.builder( physics: NeverScrollableScrollPhysics(), itemCount: newData.length, itemBuilder: (context, index) { // 每个Item独立声明颜色变量 Color back; var obj = newData[index]; // 处理oldData长度不足的情况 if (index >= oldData.length) { back = Color(0xff72bbef); return _buildRow(obj, back); } var obj2 = oldData[index]; if (obj.exchangePrices?.availableToBack?.isEmpty ?? true || obj.exchangePrices?.availableToBack?[0].size == null || obj2.exchangePrices?.availableToBack?[0].size == null) { back = Color(0xff72bbef); } else { var newAvailSize = obj.exchangePrices!.availableToBack![0].size!; var oldAvailSize = obj2.exchangePrices!.availableToBack![0].size!; if (newAvailSize > oldAvailSize) { back = Color(0xff00FFFF); // 大于时显示蓝色 } else if (newAvailSize < oldAvailSize) { back = Colors.red; // 小于时显示红色 } else { back = Colors.white; // 相等时显示白色 } } return _buildRow(obj, back); }, ), ); // 提取Row构建逻辑,简化代码 Widget _buildRow(CustomRunners obj, Color back) { return Row( children: [ Expanded( flex: 2, child: AnimatedContainer( duration: Duration(milliseconds: 50), curve: Curves.ease, height: 50, decoration: BoxDecoration( color: back, border: Border( top: BorderSide(width: 0.2, color: Colors.black), bottom: BorderSide(width: 0.2, color: Colors.black) ) ), child: Text(obj.exchangePrices?.availableToBack?[0].price?.toString() ?? ''), ), ), ], ); }
3. 优化初始化(可选)
将列表改为非空初始化,减少空判断代码:
IOWebSocketChannel? ioChannel; List<CustomRunners> oldData = []; List<CustomRunners> newData = [];
关键说明
- 数据更新顺序:必须先备份旧数据,再更新新数据,确保
oldData始终保存上一次状态,newData为最新状态,两者才有对比意义 - 变量作用域:
back变量放在itemBuilder内部,保证每个列表项的颜色独立计算,不会被其他项覆盖 - 空安全处理:增加了
oldData长度判断、空值合并运算符,避免潜在的空指针异常
内容的提问来源于stack exchange,提问作者Tintin V
相关产品推荐
相关产品推荐

