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

Flutter中如何将数组内所有OwnMessageCard的isSeen属性设为true

解决方案

你的需求要实现起来,需要先修正当前代码的两个核心问题:一是直接存储Widget不利于状态管理,二是StatelessWidget不适合承载可变状态。以下是具体实现步骤:


步骤1:创建消息数据模型

先定义一个独立的消息数据类,用来存储消息的核心信息,替代直接存储Widget的方式:

class ChatMessage {
  final String content;
  final bool isOwn; // 标记是否为自己发送的消息
  bool isSeen; // 是否已读状态
  final DateTime sentAt; // 消息发送时间

  ChatMessage({
    required this.content,
    required this.isOwn,
    required this.isSeen,
    required this.sentAt,
  });
}

步骤2:修改消息列表类型

将原来的Widget列表改为消息模型列表:

var myMessagesList = <ChatMessage>[];

步骤3:调整Socket消息接收逻辑

收到消息时,添加ChatMessage实例到列表,而非直接添加Widget:

_socket.on(myUsername, (data) {
  setState(() {
    myMessagesList.add(ChatMessage(
      content: data['message'].toString(),
      isOwn: false, // 对方发送的消息,对应ReplyCard
      isSeen: false,
      sentAt: DateTime.parse(data['sentAt'].toString()),
    ));
  });
  _socket.emit('seen', {
    'sender': myUsername,
    'target': targetUsername
  });
});

注:你自己发送消息时,也要添加isOwn: true的ChatMessage实例,示例如下:

void sendMessage(String message) {
  setState(() {
    myMessagesList.add(ChatMessage(
      content: message,
      isOwn: true,
      isSeen: false,
      sentAt: DateTime.now(),
    ));
  });
  _socket.emit('send_message', {
    'message': message,
    'sender': myUsername,
    'target': targetUsername,
    'sentAt': DateTime.now().toIso8601String(),
  });
}

步骤4:重构UI渲染逻辑

使用ListView.builder根据消息模型动态生成对应的Widget:

ListView.builder(
  itemCount: myMessagesList.length,
  itemBuilder: (context, index) {
    final message = myMessagesList[index];
    if (message.isOwn) {
      return OwnMessageCard(
        myMessage: message.content,
        isSeen: message.isSeen,
        sentAt: message.sentAt,
      );
    } else {
      return ReplyCard(
        senderMessage: message.content,
        sentAt: message.sentAt,
      );
    }
  },
)

同时调整OwnMessageCard,传入实际的发送时间(原代码用当前时间是错误的):

class OwnMessageCard extends StatelessWidget {
  const OwnMessageCard({
    super.key,
    required this.myMessage,
    required this.isSeen,
    required this.sentAt,
  });

  final String myMessage;
  final bool isSeen;
  final DateTime sentAt;

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.centerRight,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: MediaQuery.of(context).size.width - 45,
          minWidth: 180,
        ),
        child: Card(
          elevation: 1,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: ownMessageBackColor,
          margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.only(
                  left: 25,
                  right: 10,
                  top: 5,
                  bottom: 25,
                ),
                child: Text(
                  myMessage,
                  style: const TextStyle(fontSize: 18, fontFamily: 'FMitra'),
                ),
              ),
              Positioned(
                bottom: 4,
                right: 10,
                child: Row(
                  children: [
                    Icon(
                      Icons.done_all,
                      size: 20,
                      color: isSeen ? Colors.blue : Colors.grey,
                    ),
                    const SizedBox(width: 5),
                    Text(
                      "${sentAt.hour}:${sentAt.minute} | ${sentAt.year}-${sentAt.month}-${sentAt.day}",
                      style: const TextStyle(fontSize: 12, color: Colors.grey),
                    )
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

步骤5:处理Socket的seen事件

现在可以直接遍历消息列表,将所有自己发送的消息标记为已读:

_socket.on("$myUsername/seen", (data) {
  setState(() {
    for (var message in myMessagesList) {
      if (message.isOwn) {
        message.isSeen = true;
      }
    }
  });
});

当seen事件触发时,setState会通知ListView重建所有消息Widget,此时OwnMessageCard会使用更新后的isSeen值,图标颜色自动切换为蓝色。


内容的提问来源于stack exchange,提问作者Alex Wright

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:55:51