Flutter中如何计算一个Container可以完美填充多少文字

要想知道一个 Container 可以填满多少个文字,我们只需算出一行可以填充的文字数量,再算出可以填充的最大行数,将两者相乘就行。

遇到问题先干嘛?当然是用搜索引擎先检索一下答案。我检索到了一篇标题为《Flutter-如何计算文字宽高》的文章(因为我在站内没有搜到这篇文章,所有有兴趣的可以自己用搜索引擎检索查看),这篇文章使用了 TextPainter 来计算出文字的宽高。这里我就用 ParagraphBuilder 来完成计算。这可能并不是最好的,请根据需求酌情参考。

我们有如下一个示例:

显然,如果直接使用 Text 我们什么也做不了,所以我们得使用 CustomPaint 来显示绘制的文字。

dart 复制代码
class HomeView extends StatelessWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          clipBehavior: Clip.hardEdge,
          decoration: const BoxDecoration(color: Colors.green),
          child: CustomPaint(
            painter: CustomTextPainter(),
          ),
        ),
      ),
    );
  }
}

class CustomTextPainter extends CustomPainter {

  @override
  void paint(Canvas canvas, Size size) {
    // ...
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => this != oldDelegate;
}

因为不是每次传入的文字大小都是一样的,所以我们需要在外部传入该值。

dart 复制代码
class HomeView extends StatelessWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          clipBehavior: Clip.hardEdge,
          decoration: const BoxDecoration(color: Colors.green),
          child: CustomPaint(
            painter: CustomTextPainter(
              fontSize: 16,
              lineHeight: 1,
            ),
          ),
        ),
      ),
    );
  }
}

class CustomTextPainter extends CustomPainter {

  final double fontSize;
  final double lineHeight;

  CustomTextPainter({
    super.repaint,
    required this.fontSize,
    required this.lineHeight,
  });

  @override
  void paint(Canvas canvas, Size size) {
    // ...
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => this != oldDelegate;
}

这里我们指定需要传入两个参数:文字大小和行高,知道这两个参数我们就很容易通过 文字大小 * 文字行高 来计算出文字所占的高度。

计算出了文字所占的高度,就能计算出可以完美显示所有文字的行数。

代码如下:

dart 复制代码
@override
void paint(Canvas canvas, Size size) {
  ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle());
  paragraphBuilder.pushStyle(
    ui.TextStyle(
      color: Colors.white,
      fontSize: fontSize,
      height: lineHeight,
    ),
  );
  
  double fontHeight = fontSize * lineHeight;
  int fontLine = size.height ~/ fontHeight;
  print('每个文字的高度是:$fontHeight');  // 16
  print('完美呈现的文字行数是:$fontLine');  // 12
}

我们可以显示一段文字来验证下:

dart 复制代码
@override
void paint(Canvas canvas, Size size) {
  ...
    
  paragraphBuilder.addText('海水梦悠悠' * 100);
  ui.ParagraphConstraints paragraphConstraints = ui.ParagraphConstraints(width: size.width);
  ui.Paragraph paragraph = paragraphBuilder.build();
  paragraph.layout(paragraphConstraints);
  canvas.drawParagraph(paragraph, Offset.zero);
}

一共刚好 12 行。

现在知道了行数,接下来就只要算出一行可以填充几个文字就行。

想要知道一行可以填充几个文字,我们可以先算出一个字所占的宽度。我们先让界面只显示一个字:

dart 复制代码
@override
void paint(Canvas canvas, Size size) {
  ...
    
  paragraphBuilder.addText('海');
  ui.ParagraphConstraints paragraphConstraints = ui.ParagraphConstraints(width: size.width);
  ui.Paragraph paragraph = paragraphBuilder.build();
  paragraph.layout(paragraphConstraints);
  canvas.drawParagraph(paragraph, Offset.zero);
}

为什么要改成显示一个字?我们虽然无法计算出一个字的宽度,但是可以用 ParagraphcomputeLineMetrics 方法算出每一行文字所占的宽度。

dart 复制代码
@override
void paint(Canvas canvas, Size size) {
  ...
    
  paragraphBuilder.addText('海');
  ui.ParagraphConstraints paragraphConstraints = ui.ParagraphConstraints(width: size.width);
  ui.Paragraph paragraph = paragraphBuilder.build();
  paragraph.layout(paragraphConstraints);
  List<ui.LineMetrics> lines = paragraph.computeLineMetrics();
  double fontWidth = lines.first.width;
  print("第一行文字所占的宽度:$fontWidth");  // 16.000030517578125
  print("一行可以显示的文字个数:${size.width ~/ fontWidth}");  // 12
  canvas.drawParagraph(paragraph, Offset.zero);
}

为了验证一下是否准确,可以数一下下图填满时的个数:

通过以上的内容我们可以计算出,在一个 200*200 的 Container 中,可以完整显示文字大小为 16,行高为 1 的文字共 144 个。

以下是完整代码:

dart 复制代码
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

class HomeView extends StatelessWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          clipBehavior: Clip.hardEdge,
          decoration: const BoxDecoration(color: Colors.green),
          child: CustomPaint(
            painter: CustomTextPainter(
              fontSize: 16,
              lineHeight: 1,
            ),
          ),
        ),
      ),
    );
  }
}

class CustomTextPainter extends CustomPainter {
  final double fontSize;
  final double lineHeight;

  CustomTextPainter({
    super.repaint,
    required this.fontSize,
    required this.lineHeight,
  });

  @override
  void paint(Canvas canvas, Size size) {
    ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle());
    paragraphBuilder.pushStyle(
      ui.TextStyle(
        color: Colors.white,
        fontSize: fontSize,
        height: lineHeight,
      ),
    );
    double fontHeight = fontSize * lineHeight;
    int fontLine = size.height ~/ fontHeight;
    print('每个文字的高度是 $fontHeight');
    print('完美呈现的文字行数是 $fontLine');

    paragraphBuilder.addText('海');
    ui.ParagraphConstraints paragraphConstraints = ui.ParagraphConstraints(width: size.width);
    ui.Paragraph paragraph = paragraphBuilder.build();
    paragraph.layout(paragraphConstraints);

    List<ui.LineMetrics> lines = paragraph.computeLineMetrics();
    double fontWidth = lines.first.width;
    print("第一行文字所占的宽度:$fontWidth");
    print("一行可以显示的文字个数:${size.width ~/ fontWidth}");

    canvas.drawParagraph(paragraph, Offset.zero);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => this != oldDelegate;
}

当然,在实际应用中,我们不可能让"海"字显示出来,并且只能应用于中文日文之类的文字,如果插入了英文字母数字之类的可能就不准确了。

相关推荐
火柴就是我1 天前
flutter 之真手势冲突处理
android·flutter
Speed1231 天前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间1 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭1 天前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone1 天前
从flutter源码看其渲染机制
android·flutter
ALLIN2 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei2 天前
Flutter 国际化
flutter
Dabei2 天前
Flutter MQTT 通信文档
flutter
Dabei2 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉2 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter