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, ), ], )
修复效果验证
- 长文本「Party Lopes」可以完整显示,不会出现裁剪问题
- 设置
maxLines: 2时,文本会优先占满横向可用空间再换行,不会出现异常偏移 - 放在Column、Row等容器组件中可以自动适配父组件的宽度约束,也能适配系统字体缩放设置
内容的提问来源于stack exchange,提问作者Da2ny
相关产品推荐
相关产品推荐

