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
相关产品推荐
相关产品推荐

