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;
}

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

相关推荐
ujainu1 小时前
Flutter + OpenHarmony 游戏开发进阶:粒子系统初探——简易爆炸与得分飞字
flutter·游戏·openharmony
2501_944448001 小时前
Flutter for OpenHarmony衣橱管家App实战:支持我们功能实现
android·javascript·flutter
灰灰勇闯IT10 小时前
Flutter for OpenHarmony:自定义 Paint 绘图 —— 释放 Canvas 的创造力
flutter
2601_9498333910 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
牛马11112 小时前
Flutter OverlayEntry
flutter
2603_9494621012 小时前
Flutter for OpenHarmony社团管理App实战:预算管理实现
android·javascript·flutter
2601_9499757914 小时前
Flutter for OpenHarmony艺考真题题库+帮助中心实现
flutter
子春一17 小时前
Flutter for OpenHarmony:构建一个 Flutter 井字棋游戏,深入解析状态驱动逻辑、胜利判定与极简交互设计
flutter·游戏·交互
雨季66617 小时前
Flutter 三端应用实战:OpenHarmony “极简手势轨迹球”——指尖与屏幕的诗意对话
开发语言·javascript·flutter
ujainu17 小时前
Flutter + OpenHarmony 游戏开发进阶:CustomPainter 手绘游戏世界——从球体到轨道
flutter·游戏·信息可视化·openharmony