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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:37:03