【问题标题】:Flutter problem getting text-height with LineMetrics使用 LineMetrics 获取文本高度的颤振问题
【发布时间】:2020-09-02 16:33:52
【问题描述】:

我有一个应用程序在背景图像前显示带有文本的内容卡。为了更好的可读性,在文本和背景图像之间添加了一个模糊层。由于在构建之前获取小部件(或文本)的高度并不容易,所以我能找到解决这个问题的唯一方法(不使用回调或重绘)是 LineMetrics。使用 LineMetrics,我可以计算文本以正确大小绘制模糊层所需的空间。

现在问题来了:LineMetrics 的计算width 属性有时不适合呈现的文本宽度。这在导致缺少换行符的情况下是有问题的,因为模糊的背景不再覆盖整个文本区域。

我在做什么:在medium article 之后,我首先创建一个带有文本和样式的 TextSpan,然后将其添加到 TextPainter,然后使用 minWidth: 0maxWidth: 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 coloured 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 和普通文本元素进行绘制,但所描述的问题没有任何变化。

谁能帮助解决这个奇怪的行为,或者提供一种替代方法来在构建小部件之前获取文本高度?

一些相关问题:

【问题讨论】:

  • 我不明白模糊部分。你能展示一张图片吗?
  • @Suragch 我用溢出背景模糊的文本截图更新了问题。这是由文本宽度和计算的线度量不匹配造成的。

标签: flutter widget height metrics text-width


【解决方案1】:

我无法谈论您观察到的 textPainter.computeLineMetrics 的奇怪行为,但我认为您的问题有不同的解决方案,它不涉及测量文本小部件的高度。

您的content-cards 可以将StackPositionedBackdropFilter 结合使用,您可以对其进行剪裁以适合覆盖的文本。 这样做,您无需费心测量 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 以限制其尺寸。

最好的问候

【讨论】:

  • 哇,这个解决方案真的很棒。我只需要将文本从某个外部范围向下移动到背景过滤器的子级。此外,它降低了很多复杂性 - 非常感谢分享!
【解决方案2】:

编辑:事实证明,在某些情况下,此解决方案也仅适用于静态修复 (maxWidth - 10) ?

虽然我找不到颤动线度量的错误宽度计算问题的原因/解决方案,但我确实找到了在构建之前获取颤动文本小部件高度问题的解决方案:

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();

【讨论】:

    猜你喜欢
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 2023-03-16
    • 2020-02-16
    • 2021-02-01
    • 1970-01-01
    • 2020-11-25
    相关资源
    最近更新 更多