我有一个显示内容卡的应用程序,其中的文本显示在背景图像的前面。为了提高可读性,在文本和背景图像之间添加了一个模糊层。由于使用flutter在构建窗口小部件(或文本)之前先获得其高度并不容易,所以我能找到解决此问题的唯一方法(不使用回调或重绘)是LineMetrics。使用LineMetrics,我可以计算文本以正确的大小绘制模糊层所需的空间。
现在出现了问题:LineMetrics的计算出的“ width”属性有时不适合渲染的文本宽度。如果它会导致换行失败,这是有问题的,因为模糊的背景不再覆盖整个文本区域。
我在做什么:medium article之后,我首先创建具有文本和样式的TextSpan,然后将其添加到TextPainter,然后使用minWidth:0和maxWidth:maxTextWidth调用layout()函数。最后,我使用textPainter.computeLineMetrics();创建LineMetrics。
代码如下:
// Widget
@override
Widget build(BuildContext context) {
// Get text styles
final TextStyle titleStyle = TextStyle(
fontFamily: 'SF Pro Display',
fontStyle: FontStyle.normal,
fontSize: 14,
height: (17 / 14),
fontWeight: FontWeight.bold,
color: Colors.white);
final TextStyle textStyle = TextStyle(
fontFamily: 'SF Pro Display',
fontStyle: FontStyle.normal,
fontSize: 14,
height: (17 / 14),
fontWeight: FontWeight.normal,
color: Colors.white);
// Build text spans
final titleSpan = TextSpan(text: title, style: titleStyle);
final textSpan = TextSpan(text: text, style: textStyle);
// Calculate text metrics
final double _maxWidth = style.width - style.margin.horizontal;
List<LineMetrics> _titleLines = _getLineMetrics(_maxWidth, titleSpan);
List<LineMetrics> _textLines = _getLineMetrics(_maxWidth, textSpan);
// Calculate text heights
final double _titleHeight = _getLinesHeight(_titleLines) + style.margin.top;
final double _textHeight = _getLinesHeight(_textLines) + style.margin.bottom;
// Generate colored debug container
Column _titleContainer = _getTextSpanContainer(_titleLines);
Column _textContainer = _getTextSpanContainer(_textLines);
// Widget content
List<Widget> _textOverlay = [
Expanded(child: Text('')),
Stack(children: <Widget>[
Align(alignment: Alignment.topLeft, child: _titleContainer),
Align(alignment: Alignment.topLeft, child: RichText(text: titleSpan))
]),
Stack(children: <Widget>[
Align(alignment: Alignment.topLeft, child: _textContainer),
Align(alignment: Alignment.topLeft, child: RichText(text: textSpan))
])
];
List<LineMetrics> _getLineMetrics(double width, TextSpan textSpan) {
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr
);
textPainter.layout(
minWidth: 0,
maxWidth: width,
);
// TODO: width of lineMetrics sometimes not matching real text width
return textPainter.computeLineMetrics();
}
double _getLinesHeight(List<LineMetrics> lines) {
double _textHeight = 0;
for (LineMetrics line in lines) {
_textHeight += line.height;
}
return _textHeight;
}
Column _getTextSpanContainer(List<LineMetrics> lines) {
List<Widget> container = [];
for (LineMetrics line in lines) {
container.add(Container(
width: line.width,
height: line.height,
color: Colors.red,
));
}
return Column(
children: container,
crossAxisAlignment: CrossAxisAlignment.start,
);
}
我在每个文本行后面添加了具有计算得出的LineMetrics宽度的红色容器,以可视化该问题。这些屏幕截图显示了问题所在:
我尝试了TextStyles中几乎所有可能的属性(WordSpacing,LetterSpacing,textWidthBasis ...),使用和不使用自定义字体来构建它,使用RichText和普通文本Elements进行绘制,但是在所描述的问题上没有任何改变。
有人知道导致这种奇怪行为的原因吗?
答案 0 :(得分:1)
编辑:事实证明,在某些情况下,此解决方案也仅适用于静态修复 (maxWidth - 10
) ?
虽然我找不到 flutter line-metrics 错误宽度计算问题的原因/解决方案,但我确实找到了在构建之前获取 flutter text-widget 高度问题的解决方案:< /p>
extension StringExtension on String {
double getHeight(BuildContext context, RichText richText, double maxWidth) {
double maxWidthFix = maxWidth - 10; //NOTE: Fix!!!
BoxConstraints constraints = BoxConstraints(
maxWidth: maxWidthFix, // maxwidth calculated
);
RenderParagraph renderObject = richText.createRenderObject(context);
renderObject.layout(constraints);
return renderObject.getMaxIntrinsicHeight(maxWidthFix);
}
}
使用此字符串扩展,可以计算正确的文本高度以绘制相应大小的背景模糊:
// Get text heights
final double _titleHeight = title.getHeight(context, title, maxTextWidth);
final double _textHeight = text.getHeight(context, text, maxTextWidth);
final double _blurHeight = _titleHeight + _textHeight + margin;
这些屏幕截图证明使用字符串扩展时解决了最初的问题:
我根据相关主题的两个答案创建了这个解决方案:
How can I get the size of the Text Widget in flutter
对于颤振线度量,我只找到了一种解决方法,可以解决 99% 的问题?案例:
// NOTE: width of lineMetrics sometimes not matching real text width
final double lineFix = 10;
textPainter.layout(
minWidth: 0,
maxWidth: width - lineFix,
);
textPainter.computeLineMetrics();
答案 1 :(得分:1)
我不能说您观察到的 textPainter.computeLineMetrics
的奇怪行为,但我认为有一个不同的解决方案可以解决您的问题,它不涉及测量文本小部件的高度。
您的 content-cards
可以使用带有 Stack 和 Positioned 的 BackdropFilter,您可以将其剪裁以适合覆盖的文本。
这样做时,您无需费心测量 Text 小部件。
示例:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
FittedBox(
fit: BoxFit.fill,
child: Image.network('https://picsum.photos/300'),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 3.0,
sigmaY: 3.0,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: const Text(
'Aut velit illum eos aut aut eaque totam. Autem aut quis omnis et minus. Itaque at molestias enim sunt autem voluptas voluptatem delectus. Minima deleniti fugiat sit sunt fugiat. Cumque iusto quo eum. Ipsa laborum est qui.'),
),
),
),
),
],
);
}
}
<块引用>
注意:此示例呈现扩展的堆栈。对于您的内容卡,您可能会使用例如一个 SizedBox 来限制它的维度。
最好的问候