Flutter中如何计算一个Container可以完美填充多少文字
要想知道一个 Container 可以填满多少个文字,我们只需算出一行可以填充的文字数量,再算出可以填充的最大行数,将两者相乘就行。
遇到问题先干嘛?当然是用搜索引擎先检索一下答案。我检索到了一篇标题为《Flutter-如何计算文字宽高》的文章(因为我在站内没有搜到这篇文章,所有有兴趣的可以自己用搜索引擎检索查看),这篇文章使用了 TextPainter 来计算出文字的宽高。这里我就用 ParagraphBuilder 来完成计算。这可能并不是最好的,请根据需求酌情参考。
我们有如下一个示例:
显然,如果直接使用 Text 我们什么也做不了,所以我们得使用 CustomPaint 来显示绘制的文字。
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;
}
因为不是每次传入的文字大小都是一样的,所以我们需要在外部传入该值。
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;
}
这里我们指定需要传入两个参数:文字大小和行高,知道这两个参数我们就很容易通过 文字大小 * 文字行高 来计算出文字所占的高度。
计算出了文字所占的高度,就能计算出可以完美显示所有文字的行数。
代码如下:
@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
}
我们可以显示一段文字来验证下:
@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 行。
现在知道了行数,接下来就只要算出一行可以填充几个文字就行。
想要知道一行可以填充几个文字,我们可以先算出一个字所占的宽度。我们先让界面只显示一个字:
@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);
}
为什么要改成显示一个字?我们虽然无法计算出一个字的宽度,但是可以用 Paragraph 的 computeLineMetrics 方法算出每一行文字所占的宽度。
@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 个。
以下是完整代码:
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;
}
当然,在实际应用中,我们不可能让 “海” 字显示出来,并且只能应用于中文日文之类的文字,如果插入了英文字母数字之类的可能就不准确了。