Baseline 是布局 widget,作用是让 child 的基线和指定的位置重合。Baseline 的布局逻辑不难,很多同学不知道如何用这个 widget 是因为对基线不是很理解。下面我们先看一下基线。
基线(英语:Baseline)指的是多数拉丁字母排列的基准线。如上图所示,大多字母都沿着红色基线排列。
baseline 理解了,但是 Baseline widget 还有一个参数 baselineType,这个参数是一枚举类型。
/// A horizontal line used for aligning text. enum TextBaseline { /// The horizontal line used to align the bottom of glyphs for alphabetic characters. alphabetic, /// The horizontal line used to align ideographic characters. ideographic, } 复制代码
把注释翻译一下
// 一条用于对齐文本的水平线 enum TextBaseline { /// 用于对齐字母字符字形底部的水平线 alphabetic, /// 用于对齐表意字符的水平线 ideographic, } 复制代码
在英语中把中文,韩文等东亚文字称为 ideographic(表意)文字。比如 Fluter Widget 天天更新
这几个字放在一起的时候,基线就可以有两个 ,一个是 Fluter Widget
的基线,一个是 天天更新
的基线。
这样说了半天,可能还是不能理解,下面我们自己把这两条基线划出来。上面的是字母基线,下面的是表意基线
class myPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final textStyle = TextStyle( color: Colors.black, fontSize: 30, ); final textSpan = TextSpan( text: 'Flutter widget每日更新IAM17', style: textStyle, ); final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, ); textPainter.layout( minWidth: 0, maxWidth: size.width, ); final paint = Paint() ..color = Colors.red ..style = PaintingStyle.stroke ..strokeWidth = 1; // 把 TextBaseline.ideographic 改为 TextBaseline.alphabetic 画字母基线。 final distanceToBaseline = textPainter.computeDistanceToActualBaseline(TextBaseline.ideographic); canvas.drawLine( Offset(0, distanceToBaseline), Offset(textPainter.width, distanceToBaseline), paint, ); final offset = Offset(0, 0); textPainter.paint(canvas, offset); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } 复制代码
关于基线的知识了解了,Baseline 就变得非常简单了。
布局逻辑的代码
void performLayout() { if (child != null) { final BoxConstraints constraints = this.constraints; child!.layout(constraints.loosen(), parentUsesSize: true); final double childBaseline = child!.getDistanceToBaseline(baselineType)!; final double actualBaseline = baseline; final double top = actualBaseline - childBaseline; final BoxParentData childParentData = child!.parentData! as BoxParentData; childParentData.offset = Offset(0.0, top); final Size childSize = child!.size; size = constraints.constrain(Size(childSize.width, top + childSize.height)); } else { size = constraints.smallest; } } 复制代码
Baseline 就是让它指定的基线位置和 child 的基线重合。 我们通过示例看下。
Center(child:Container( decoration: BoxDecoration(border: Border.all(color: Colors.red)), child: Baseline( baseline: 100, baselineType: TextBaseline.ideographic, child: Container( width: 100, height: 100, color: Colors.blue[200], )))); 复制代码
我们给 Baseline 套上一个红框,用来显示它的大小。
- baseline 与 child baseline 值相同
在本例中 child baseline 为 100,与指定的baseline 相同,结果是 size 与 child size 相同。Baseline紧紧包裹住 child。
非文字的基线位置是 box 的底部
- baseline 小于 child baseline
baseline: 100 修改为 baseline: 0
size 为0。chld 在 baseline 正上方。 3. baseline 大于 child baseline
baseline: 100 修改为 baseline: 150
size 为 150,Baseline 完全包裹 child。child 在 Baseline 底部
- baseline 可以为负值
baseline: 100 修改为 baseline: -10
size 为 0 child 在 Baseline 上方,和 Baseline 间隔 baseline 距离。
通过这样几个示例,我们对 Baseline 的作用已经有了感观上的认识。下面思考一下它的应用场景。
Baseline 的应用场景
通过上面的例子我们可以看出 Baseline 可以做溢出效果,但如果是溢出,最好还是用 OverflowBox。Baseline 的应用应该是在文字对齐方面。比如要实现下面的效果,"IAM17” 这几个字要求紧贴红框下边。
Container( height: 50, decoration: BoxDecoration(border: Border.all(color: Colors.red)), child: Baseline( baseline: 50, baselineType: TextBaseline.ideographic, child: Text('IAM17', style: TextStyle(fontSize: 24,)), ), ) 复制代码
你可能会说,如果不贴底,可以设置一个偏移,但这个偏移是多少呢?随着字体,字号的变化,这个偏移量也是变化的。
用 Baseline 可以轻松实现基线对齐,遇到基线对齐的问题,应该第一时间想到 Baseline。