Flutter:实现文本后方粗下划线,需保留换行及滚动正常行为求方案
自定义文本粗下划线解决方案(保留换行与滚动)
问题背景
需要给指定文本段添加自定义粗下划线,要求不破坏文本自动换行特性,但尝试两种方案均存在问题:
- 方案1:在
TextSpan上使用CustomPainter绘制装饰
缺陷:可与其他TextSpan组合使用,但无法正确处理文本换行后的下划线绘制。 - 方案2:在
TextStyle的background属性中使用Paint和渐变着色器
缺陷:换行表现正常,但滚动时下划线会出现偏移异常。
可行解决方案
方案1:自定义InlineSpan实现逐行精准下划线
通过继承TextSpan并重写paint方法,利用文本布局提供的每行位置信息,逐行绘制下划线,既保留文本换行逻辑,也能保证滚动时的正常表现。
实现代码
class UnderlineTextSpan extends TextSpan { final Color underlineColor; final double underlineThickness; final double underlineOffset; UnderlineTextSpan({ required String text, required TextStyle style, this.underlineColor = Colors.red, this.underlineThickness = 4.0, this.underlineOffset = 2.0, }) : super(text: text, style: style); @override void paint(Canvas canvas, Offset offset, TextPainter painter, Offset? caretOffset, Rect? caretPrototype, TextBoxSelection? selection, {required List<TextBox> textBoxes, required Rect? paintBounds}) { // 先绘制原文本 super.paint(canvas, offset, painter, caretOffset, caretPrototype, selection, textBoxes: textBoxes, paintBounds: paintBounds); // 遍历每行文本框,绘制下划线 for (final textBox in textBoxes) { final underlineRect = Rect.fromLTWH( textBox.left + offset.dx, textBox.bottom + offset.dy + underlineOffset, textBox.width, underlineThickness, ); canvas.drawRect(underlineRect, Paint()..color = underlineColor); } } } // 使用示例 class UnderlineText extends StatelessWidget { const UnderlineText({super.key}); static const data1 = 'This is un-highlighted text'; static const data2 = ' and this is a very long highlighted text to test if ' 'highlight would wrap itself when text folds.'; static const data3 = "Ascent is the font's spacing above the baseline " "without leading and descent is the spacing below the baseline without " "leading. Leading is split evenly between the top and bottom. The values " "for ascent and descent are provided by the font named by fontFamily."; static const data4 = " If no fontFamily or fontFamilyFallback is provided, then the platform's " "default family will be used. Many fonts will have leading values of zero, " "so in practice, the leading component is often irrelevant."; @override Widget build(BuildContext context) { final font = Theme.of(context).textTheme.headlineMedium; return RichText( text: TextSpan( style: font, children: [ TextSpan(text: data1), UnderlineTextSpan( text: data2 + data3, style: font!, underlineColor: Colors.red, underlineThickness: 4.0, underlineOffset: 2.0, ), TextSpan(text: data4), ], ), ); } }
方案优势
- 完全适配文本自动换行逻辑,每一行文本下方都会精准绘制下划线。
- 滚动时基于当前实时布局绘制,不会出现偏移或错位问题。
- 可灵活调整下划线的颜色、厚度和与文本的偏移量。
方案2:使用原生TextDecoration快速实现
如果需求不需要高度自定义的下划线样式,可直接利用TextStyle的decoration属性实现,代码更简洁:
TextSpan( text: data2 + data3, style: font?.copyWith( decoration: TextDecoration.underline, decorationColor: Colors.red, decorationThickness: 4.0, decorationStyle: TextDecorationStyle.solid, decorationOffset: const TextDecorationOffset(2.0), ), )
这种方式原生支持换行,滚动表现正常,但下划线样式的自定义程度相对有限。
内容的提问来源于stack exchange,提问作者Ashutosh Singh
相关产品推荐
相关产品推荐

