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

Flutter如何实现文本中间显示TextOverflow.ellipsis效果

实现父宽度驱动的文本中间省略效果

输入图片描述

需求:实现如图所示的UI效果,该效果不受字符串长度影响,由包含它的父Widget宽度决定。

调研情况:曾查找过同类问题但未找到有效解决方案;了解过extended_text包,但不想为该小功能引入额外依赖。

解决方案:自定义原生Widget实现

无需引入第三方包,通过Flutter原生API即可实现,核心思路是利用LayoutBuilder获取父容器宽度,结合TextPainter计算文本尺寸,动态截取前后文本并拼接省略号。

以下是完整可复用的代码实现:

import 'package:flutter/material.dart';

class MiddleEllipsisText extends StatelessWidget {
  final String text;
  final TextStyle? style;
  final String ellipsis;

  const MiddleEllipsisText({
    super.key,
    required this.text,
    this.style,
    this.ellipsis = '...',
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final maxWidth = constraints.maxWidth;
        if (maxWidth <= 0 || text.isEmpty) {
          return Text(text, style: style);
        }

        // 计算省略号宽度
        final ellipsisPainter = TextPainter(
          text: TextSpan(text: ellipsis, style: style),
          textDirection: TextDirection.ltr,
        )..layout(maxWidth: double.infinity);
        final ellipsisWidth = ellipsisPainter.width;

        // 文本本身宽度未超过父容器,直接显示原文本
        final fullTextPainter = TextPainter(
          text: TextSpan(text: text, style: style),
          textDirection: TextDirection.ltr,
        )..layout(maxWidth: double.infinity);
        if (fullTextPainter.width <= maxWidth) {
          return Text(text, style: style);
        }

        // 计算前后文本可使用的总宽度
        final availableWidth = maxWidth - ellipsisWidth;
        final halfWidth = availableWidth / 2;

        // 确定前缀最大可显示长度
        int prefixLen = 0;
        for (int i = 1; i <= text.length; i++) {
          final prefix = text.substring(0, i);
          final painter = TextPainter(
            text: TextSpan(text: prefix, style: style),
            textDirection: TextDirection.ltr,
          )..layout(maxWidth: double.infinity);
          if (painter.width > halfWidth) {
            prefixLen = i - 1;
            break;
          }
          if (i == text.length) prefixLen = i;
        }

        // 确定后缀最大可显示长度
        int suffixLen = 0;
        for (int i = 1; i <= text.length - prefixLen; i++) {
          final suffix = text.substring(text.length - i);
          final painter = TextPainter(
            text: TextSpan(text: suffix, style: style),
            textDirection: TextDirection.ltr,
          )..layout(maxWidth: double.infinity);
          if (painter.width > halfWidth) {
            suffixLen = i - 1;
            break;
          }
          if (i == text.length - prefixLen) suffixLen = i;
        }

        // 极端情况处理:省略号占满父容器
        if (prefixLen == 0 && suffixLen == 0) {
          return Text(ellipsis, style: style);
        }

        final displayText = '${text.substring(0, prefixLen)}'
            '$ellipsis'
            '${text.substring(text.length - suffixLen)}';

        return Text(displayText, style: style);
      },
    );
  }
}

// 使用示例
void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(
        child: SizedBox(
          width: 200, // 父容器宽度决定截断效果
          child: MiddleEllipsisText(
            text: '这是一段需要在中间显示省略号的长文本内容',
            style: TextStyle(fontSize: 16),
          ),
        ),
      ),
    ),
  ));
}

关键说明:

  • 父容器宽度驱动:通过LayoutBuilder实时获取父Widget的约束宽度,确保截断逻辑完全依赖父容器尺寸,不受字符串长度直接影响
  • 精确尺寸计算:用TextPainter计算文本和省略号的实际渲染宽度,避免视觉上的溢出问题
  • 均衡截断:优先将剩余宽度平均分配给前后文本,保证显示效果更协调
  • 边界覆盖:处理了文本过短、省略号占满父容器等极端场景,确保鲁棒性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:30:02