You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 15:13:21