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

Flutter聊天气泡长消息溢出问题及多行显示与样式切换需求

聊天气泡长消息适配与单行/多行样式切换方案

一、解决长消息溢出问题

当前代码中聊天气泡在长消息时出现溢出,核心原因有两点:

  • Text组件默认仅显示1行,超出部分会被截断
  • 外层Container在Row中无宽度限制,长文本会直接撑破容器

修改方案:

  1. 给消息文本添加自动换行配置,允许多行显示
  2. 限制聊天气泡的最大宽度,避免占满整个屏幕

二、实现单行/多行样式切换

要判断文本是单行还是多行,需通过TextPainter计算文本渲染后的行数,再根据行数动态调整聊天气泡的样式(比如圆角、阴影、背景色等)。由于需要计算状态,需将原StatelessWidget改为StatefulWidget。

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

class MessageCardWidget extends StatefulWidget {
  final MessageModel msgmodel;
  final bool sendbyme;

  const MessageCardWidget({
    Key? key,
    required this.msgmodel,
    required this.sendbyme,
  }) : super(key: key);

  @override
  State<MessageCardWidget> createState() => _MessageCardWidgetState();
}

class _MessageCardWidgetState extends State<MessageCardWidget> {
  // 计算文本是否为多行
  bool _isMultiLine(String text, TextStyle style, double maxWidth) {
    final textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      maxLines: null,
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: maxWidth);

    return textPainter.lineCount > 1;
  }

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final maxBubbleWidth = screenWidth * 0.7; // 限制气泡最大宽度为屏幕70%
    final messageTextStyle = TextStyle(fontSize: 20, color: Colors.black);
    final isMultiLine = _isMultiLine(
      widget.msgmodel.msg.toString(),
      messageTextStyle,
      maxBubbleWidth - 40, // 减去左右padding的20*2
    );

    return Padding(
      padding: EdgeInsets.only(bottom: 20, left: 40),
      child: Row(
        mainAxisAlignment: widget.sendbyme
            ? MainAxisAlignment.end
            : MainAxisAlignment.start,
        children: [
          ConstrainedBox(
            constraints: BoxConstraints(maxWidth: maxBubbleWidth),
            child: Container(
              decoration: BoxDecoration(
                boxShadow: isMultiLine
                    ? [
                        // 多行消息使用不同阴影
                        BoxShadow(
                          color: Color.fromRGBO(0, 0, 255, 0.3),
                          offset: Offset(2, 2),
                          spreadRadius: 3,
                          blurRadius: 2,
                        )
                      ]
                    : const [
                        BoxShadow(
                          color: Color.fromRGBO(0, 0, 255, 0.2),
                          offset: Offset(1, 1),
                          spreadRadius: 2,
                          blurRadius: 1,
                        )
                      ],
                borderRadius: isMultiLine
                    ? widget.sendbyme
                        ? BorderRadius.only(
                            topLeft: Radius.circular(10),
                            topRight: Radius.circular(10),
                            bottomLeft: Radius.circular(10),
                            bottomRight: Radius.circular(0),
                          )
                        : BorderRadius.only(
                            topLeft: Radius.circular(10),
                            topRight: Radius.circular(10),
                            bottomLeft: Radius.circular(0),
                            bottomRight: Radius.circular(10),
                          )
                    : BorderRadius.circular(10),
                color: isMultiLine
                    ? widget.sendbyme
                        ? Colors.green[200]
                        : Colors.grey[100]
                    : widget.sendbyme
                        ? Colors.green[100]
                        : Colors.white,
              ),
              padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
              child: Column(
                crossAxisAlignment: widget.sendbyme
                    ? CrossAxisAlignment.end
                    : CrossAxisAlignment.start,
                children: [
                  Text(
                    widget.msgmodel.msg.toString(),
                    style: messageTextStyle,
                    maxLines: null, // 允许文本自动换行
                    softWrap: true, // 启用软换行
                  ),
                  SizedBox(height: 2),
                  Text(
                    DateFormat('hh-mm').format(widget.msgmodel.createdon!).toString(),
                    style: TextStyle(fontSize: 12, color: Colors.grey),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// 项目中实际的MessageModel类可根据需求调整
class MessageModel {
  final String? msg;
  final DateTime? createdon;

  MessageModel({this.msg, this.createdon});
}

关键修改说明

  1. 长消息适配:

    • 用ConstrainedBox限制气泡最大宽度,避免文本过宽
    • 给Text添加maxLines: null和softWrap: true,实现自动多行显示
  2. 单行/多行样式切换:

    • 通过TextPainter计算文本在指定宽度下的行数
    • 根据行数动态切换气泡的阴影、圆角、背景色等样式

内容的提问来源于stack exchange,提问作者Irfan Ganatra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 23:55:19