将容器与文本的顶部和底部对齐
Align Container to top and bottom of Text
我正在尝试将一个彩色矩形对齐到两个文本小部件的左侧。具体来说,我希望彩色矩形的底部与下方文本的基线对齐,矩形的顶部与上方文本的大写高度对齐。这是我要实现的目标的模拟:
到目前为止我的代码:
final TextStyle helloTextStyle = const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 28,
letterSpacing: 0,
wordSpacing: 0,
fontFamily: "DejaVuSansCondensed",
color: Color(0XFF232444),
decoration: TextDecoration.none,
);
final TextStyle everyoneTextStyle = const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 38,
letterSpacing: 0,
fontFamily: "DejaVuSansCondensed",
color: Color(0XFF232444),
decoration: TextDecoration.none,
);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0,
color: Colors.red),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
),
],
)
如何将彩色矩形的底部与下方文本的基线对齐,并将矩形的顶部与上方文本的大写高度对齐?
编辑:一种解决方案是确定基线与文本小部件底部之间的距离以及大写高度与文本小部件顶部之间的距离。文本小部件似乎没有提供这些值。
将您 Row
包裹在 Container
中,其 height
属性 已定义:
Container(
height: MediaQuery.of(context).size.height/4 ,
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: 16.0,
color: Colors.red,
),
]
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
]
),
]
),
),
您不需要像您那样将 Column
作为 Container
的子级。这样它就不会使用文本作为基线。
一种方法:
double fontSize1 = 38.0;
double fontSize2 = 28.0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top:(fontSize2 / 4.5),bottom: (fontSize1 / 4.2)),
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0,
color: Colors.red),
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
],
),
),
),
);
}
输出:
与:
double fontSize1 = 68.0;
double fontSize2 = 18.0;
输出:
所以填充现在是自动计算的。
没有 Flutter API 可以获取文本的精确边界。 Flutter: finding the exact bounds of text 涵盖了这一点。也就是说,我有一个基于相同讨论的解决方案。
方法是将字符(在我的例子中是大写 'I')绘制到 canvas,然后扫描图像像素以寻找字符的边缘。我计算字符和图像边缘之间的像素行,并使用它来设置彩色块上的填充。我的解决方案有点复杂,因为我在 Column 中有两个 Text 小部件,每个 Text 的大小不同。
注意:除非您非常关心与字符边缘的精确对齐,否则我不建议使用此解决方案。
布局代码:
IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FutureBuilder<TopBottomPadding>(
future: _calcPadding(
TextSpan(
text: "I", style: helloTextStyle),
TextSpan(
text: "I", style: everyoneTextStyle),
mediaQueryData.textScaleFactor),
builder: (BuildContext context, tuple) {
return Padding(
padding: EdgeInsets.only(
top: tuple.data.top,
bottom: tuple.data.bottom,
),
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0, color: Colors.red),
),
),
),
);
}),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
],
),
)
生成图像涉及异步调用,因此我使用了 FutureBuilder 小部件。
Future<TopBottomPadding> _calcPadding(final TextSpan topSpan,
final TextSpan bottomSpan, final double textScaleFactor) async {
final topPadding = await _calcTopPadding(topSpan, textScaleFactor);
final bottomPadding = await _calcBottomPadding(bottomSpan, textScaleFactor);
return TopBottomPadding(topPadding, bottomPadding);
}
Future<double> _calcTopPadding(TextSpan span, double textScaleFactor) async {
final int bytesPerPixel = 4;
final imageData =
await _getImageByteData(span, ImageByteFormat.rawRgba, textScaleFactor);
final Size imageSize = imageData.size;
final ByteData byteData = imageData.byteData;
final numRows =
(byteData.lengthInBytes / (bytesPerPixel * imageSize.width)).round();
int foundRow;
/// Scan each pixel from top to bottom keeping track of the row
for (int row = 0; row < numRows && foundRow == null; row++) {
final int rowLength = bytesPerPixel * imageSize.width.round();
final int startRowByteIndex = row * rowLength;
/// Only looking at first byte of each pixel is good enough
for (int byteArrayIndex = startRowByteIndex;
byteArrayIndex < row * rowLength + rowLength;
byteArrayIndex += bytesPerPixel) {
final int byteValue = byteData.getUint8(byteArrayIndex);
/// The background is white so look for a non-white pixel.
if (foundRow == null && byteValue != 0xff) {
foundRow = row;
break;
}
}
}
final double result = foundRow == null ? 0 : foundRow.toDouble();
return result;
}
Future<double> _calcBottomPadding(
final TextSpan span, final textScaleFactor) async {
final int bytesPerPixel = 4;
final imageData =
await _getImageByteData(span, ImageByteFormat.rawRgba, textScaleFactor);
final Size imageSize = imageData.size;
final ByteData byteData = imageData.byteData;
final numRows =
(byteData.lengthInBytes / (bytesPerPixel * imageSize.width)).round();
int foundRow;
/// Scan each pixel from bottom to top keeping track of the row
for (int row = numRows - 1; row >= 0 && foundRow == null; row--) {
final int rowLength = bytesPerPixel * imageSize.width.round();
final int startRowByteIndex = row * rowLength;
/// Only looking at first byte of each pixel is good enough
for (int byteArrayIndex = startRowByteIndex;
byteArrayIndex < row * rowLength + rowLength;
byteArrayIndex += bytesPerPixel) {
final int byteValue = byteData.getUint8(byteArrayIndex);
/// The background is white so look for a non-white pixel.
if (foundRow == null && byteValue != 0xff) {
foundRow = row;
break;
}
}
}
final double foundRowIndex = foundRow == null ? 0 : foundRow.toDouble();
final int heightAsZeroBasedIndex = imageSize.height.round() - 1;
final double paddingValue = heightAsZeroBasedIndex - foundRowIndex;
return paddingValue;
}
Future<ImageData> _getImageByteData(final TextSpan span,
final ImageByteFormat byteFormat, final double textScaleFactor) async {
final painter = TextPainter(
text: span,
textDirection: TextDirection.ltr,
textScaleFactor: textScaleFactor);
painter.layout();
final imageData = ImageData();
imageData.size = Size(painter.width, painter.height);
final recorder = PictureRecorder();
final screen = Offset.zero & imageData.size;
final canvas = Canvas(recorder);
drawBackground(canvas, screen);
painter.paint(canvas, Offset.zero);
final picture = recorder.endRecording();
final image =
await picture.toImage(screen.width.round(), screen.height.round());
final ByteData byteData = await image.toByteData(format: byteFormat);
imageData.byteData = byteData;
return imageData;
}
void drawBackground(final Canvas canvas, final Rect screen) {
canvas.drawRect(
screen,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
}
class TopBottomPadding {
double top;
double bottom;
TopBottomPadding(this.top, this.bottom);
}
class ImageData {
ByteData byteData;
Size size;
}
此解决方案适用于任何屏幕密度、字体大小或文本比例因子。
我正在尝试将一个彩色矩形对齐到两个文本小部件的左侧。具体来说,我希望彩色矩形的底部与下方文本的基线对齐,矩形的顶部与上方文本的大写高度对齐。这是我要实现的目标的模拟:
final TextStyle helloTextStyle = const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 28,
letterSpacing: 0,
wordSpacing: 0,
fontFamily: "DejaVuSansCondensed",
color: Color(0XFF232444),
decoration: TextDecoration.none,
);
final TextStyle everyoneTextStyle = const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 38,
letterSpacing: 0,
fontFamily: "DejaVuSansCondensed",
color: Color(0XFF232444),
decoration: TextDecoration.none,
);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0,
color: Colors.red),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
),
],
)
如何将彩色矩形的底部与下方文本的基线对齐,并将矩形的顶部与上方文本的大写高度对齐?
编辑:一种解决方案是确定基线与文本小部件底部之间的距离以及大写高度与文本小部件顶部之间的距离。文本小部件似乎没有提供这些值。
将您 Row
包裹在 Container
中,其 height
属性 已定义:
Container(
height: MediaQuery.of(context).size.height/4 ,
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: 16.0,
color: Colors.red,
),
]
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
]
),
]
),
),
您不需要像您那样将 Column
作为 Container
的子级。这样它就不会使用文本作为基线。
一种方法:
double fontSize1 = 38.0;
double fontSize2 = 28.0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top:(fontSize2 / 4.5),bottom: (fontSize1 / 4.2)),
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0,
color: Colors.red),
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
],
),
),
),
);
}
输出:
与:
double fontSize1 = 68.0;
double fontSize2 = 18.0;
输出:
所以填充现在是自动计算的。
没有 Flutter API 可以获取文本的精确边界。 Flutter: finding the exact bounds of text 涵盖了这一点。也就是说,我有一个基于相同讨论的解决方案。
方法是将字符(在我的例子中是大写 'I')绘制到 canvas,然后扫描图像像素以寻找字符的边缘。我计算字符和图像边缘之间的像素行,并使用它来设置彩色块上的填充。我的解决方案有点复杂,因为我在 Column 中有两个 Text 小部件,每个 Text 的大小不同。
注意:除非您非常关心与字符边缘的精确对齐,否则我不建议使用此解决方案。
布局代码:
IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FutureBuilder<TopBottomPadding>(
future: _calcPadding(
TextSpan(
text: "I", style: helloTextStyle),
TextSpan(
text: "I", style: everyoneTextStyle),
mediaQueryData.textScaleFactor),
builder: (BuildContext context, tuple) {
return Padding(
padding: EdgeInsets.only(
top: tuple.data.top,
bottom: tuple.data.bottom,
),
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 16.0, color: Colors.red),
),
),
),
);
}),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("HELLO", style: helloTextStyle),
Text("EVERYONE", style: everyoneTextStyle),
],
),
],
),
)
生成图像涉及异步调用,因此我使用了 FutureBuilder 小部件。
Future<TopBottomPadding> _calcPadding(final TextSpan topSpan,
final TextSpan bottomSpan, final double textScaleFactor) async {
final topPadding = await _calcTopPadding(topSpan, textScaleFactor);
final bottomPadding = await _calcBottomPadding(bottomSpan, textScaleFactor);
return TopBottomPadding(topPadding, bottomPadding);
}
Future<double> _calcTopPadding(TextSpan span, double textScaleFactor) async {
final int bytesPerPixel = 4;
final imageData =
await _getImageByteData(span, ImageByteFormat.rawRgba, textScaleFactor);
final Size imageSize = imageData.size;
final ByteData byteData = imageData.byteData;
final numRows =
(byteData.lengthInBytes / (bytesPerPixel * imageSize.width)).round();
int foundRow;
/// Scan each pixel from top to bottom keeping track of the row
for (int row = 0; row < numRows && foundRow == null; row++) {
final int rowLength = bytesPerPixel * imageSize.width.round();
final int startRowByteIndex = row * rowLength;
/// Only looking at first byte of each pixel is good enough
for (int byteArrayIndex = startRowByteIndex;
byteArrayIndex < row * rowLength + rowLength;
byteArrayIndex += bytesPerPixel) {
final int byteValue = byteData.getUint8(byteArrayIndex);
/// The background is white so look for a non-white pixel.
if (foundRow == null && byteValue != 0xff) {
foundRow = row;
break;
}
}
}
final double result = foundRow == null ? 0 : foundRow.toDouble();
return result;
}
Future<double> _calcBottomPadding(
final TextSpan span, final textScaleFactor) async {
final int bytesPerPixel = 4;
final imageData =
await _getImageByteData(span, ImageByteFormat.rawRgba, textScaleFactor);
final Size imageSize = imageData.size;
final ByteData byteData = imageData.byteData;
final numRows =
(byteData.lengthInBytes / (bytesPerPixel * imageSize.width)).round();
int foundRow;
/// Scan each pixel from bottom to top keeping track of the row
for (int row = numRows - 1; row >= 0 && foundRow == null; row--) {
final int rowLength = bytesPerPixel * imageSize.width.round();
final int startRowByteIndex = row * rowLength;
/// Only looking at first byte of each pixel is good enough
for (int byteArrayIndex = startRowByteIndex;
byteArrayIndex < row * rowLength + rowLength;
byteArrayIndex += bytesPerPixel) {
final int byteValue = byteData.getUint8(byteArrayIndex);
/// The background is white so look for a non-white pixel.
if (foundRow == null && byteValue != 0xff) {
foundRow = row;
break;
}
}
}
final double foundRowIndex = foundRow == null ? 0 : foundRow.toDouble();
final int heightAsZeroBasedIndex = imageSize.height.round() - 1;
final double paddingValue = heightAsZeroBasedIndex - foundRowIndex;
return paddingValue;
}
Future<ImageData> _getImageByteData(final TextSpan span,
final ImageByteFormat byteFormat, final double textScaleFactor) async {
final painter = TextPainter(
text: span,
textDirection: TextDirection.ltr,
textScaleFactor: textScaleFactor);
painter.layout();
final imageData = ImageData();
imageData.size = Size(painter.width, painter.height);
final recorder = PictureRecorder();
final screen = Offset.zero & imageData.size;
final canvas = Canvas(recorder);
drawBackground(canvas, screen);
painter.paint(canvas, Offset.zero);
final picture = recorder.endRecording();
final image =
await picture.toImage(screen.width.round(), screen.height.round());
final ByteData byteData = await image.toByteData(format: byteFormat);
imageData.byteData = byteData;
return imageData;
}
void drawBackground(final Canvas canvas, final Rect screen) {
canvas.drawRect(
screen,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
}
class TopBottomPadding {
double top;
double bottom;
TopBottomPadding(this.top, this.bottom);
}
class ImageData {
ByteData byteData;
Size size;
}
此解决方案适用于任何屏幕密度、字体大小或文本比例因子。