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

Flutter 2 Column中基于TextPainter的渐变文本显示异常修复求助

问题根因
  • 测量与绘制的文本方向不一致:组件build阶段测量尺寸用的TextPainter设置了TextDirection.rtl(从右到左),但绘制阶段用的是TextDirection.ltr(从左到右),二者对空格、西文字符的宽度计算存在差异,导致测量得到的CustomPaint尺寸小于实际绘制所需的尺寸,长文本就会被裁剪。
  • 测量未考虑父组件约束:当前直接在build方法中调用TextPainter.layout()时没有传入最大宽度参数,默认最大宽度为无穷大,当文本宽度超过Column的可用宽度时,测量得到的尺寸超出父组件约束,会被强制裁剪,同时触发不必要的换行。
  • 适配逻辑缺失:没有获取父组件的宽度约束,也没有适配系统字体缩放参数,不同设备、不同字体大小下容易出现显示异常。
  • 重绘判断不完整:shouldRepaint方法没有判断最大行数、字体缩放因子的变化,参数修改后不会触发重绘。
修复后完整代码
import 'package:flutter/material.dart';

class GradientText extends StatelessWidget {
  final String text;
  final TextStyle style;
  final Gradient? gradient;
  final int? maxLines;

  const GradientText(
    this.text, {
    required this.gradient,
    this.style = const TextStyle(),
    this.maxLines = 1,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 用LayoutBuilder获取父组件的宽度约束
    return LayoutBuilder(
      builder: (context, constraints) {
        final textScaleFactor = MediaQuery.of(context).textScaleFactor;
        final TextPainter _painter = TextPainter(
          maxLines: maxLines,
          // 统一用ltr和绘制阶段保持一致
          textDirection: TextDirection.ltr,
          text: TextSpan(text: text, style: style),
          textScaleFactor: textScaleFactor,
        );
        // 传入父组件的宽度约束进行测量
        _painter.layout(
          minWidth: constraints.minWidth,
          maxWidth: constraints.maxWidth,
        );
        return CustomPaint(
          size: _painter.size,
          painter: _GradientTextPainter(
            text: text,
            style: style,
            gradient: gradient,
            maxLines: maxLines,
            textScaleFactor: textScaleFactor,
          ),
        );
      },
    );
  }
}

class _GradientTextPainter extends CustomPainter {
  final Gradient? gradient;
  final String? text;
  final TextStyle? style;
  final int? maxLines;
  final double textScaleFactor;

  _GradientTextPainter({
    Listenable? repaint,
    required this.text,
    required this.style,
    required this.gradient,
    required this.maxLines,
    required this.textScaleFactor,
  }) : super(repaint: repaint);

  @override
  void paint(Canvas canvas, Size size) {
    final Paint _gradientShaderPaint = Paint()
      ..shader = gradient != null
          ? gradient!.createShader(Offset.zero & size)
          : null;

    final TextPainter _textPainter = TextPainter(
      maxLines: maxLines,
      textDirection: TextDirection.ltr,
      textScaleFactor: textScaleFactor,
      text: TextSpan(
        text: text!,
        style: style?.copyWith(foreground: _gradientShaderPaint) ?? TextStyle(foreground: _gradientShaderPaint),
      ),
    );
    _textPainter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );
    _textPainter.paint(
      canvas,
      Offset(
        (size.width - _textPainter.width) / 2,
        (size.height - _textPainter.height) / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(_GradientTextPainter oldDelegate) {
    return gradient != oldDelegate.gradient ||
        text != oldDelegate.text ||
        style != oldDelegate.style ||
        maxLines != oldDelegate.maxLines ||
        textScaleFactor != oldDelegate.textScaleFactor;
  }
}
调用示例
Column(
  children: [
    GradientText(
      'Party Lopes',
      gradient: const LinearGradient(
        colors: <Color>[
          Color.fromARGB(255, 227, 82, 0),
          Color.fromARGB(255, 244, 176, 0)
        ],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
      style: TextStyle(
        fontWeight: FontWeight.w500
      ),
      maxLines: 1,
    ),
  ],
)
修复效果验证
  1. 长文本「Party Lopes」可以完整显示,不会出现裁剪问题
  2. 设置maxLines: 2时,文本会优先占满横向可用空间再换行,不会出现异常偏移
  3. 放在Column、Row等容器组件中可以自动适配父组件的宽度约束,也能适配系统字体缩放设置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 15:06:02