深色模式
RenderFlex 源码分析
RenderFlex
_getIntrinsicSize()
这是一个用于计算Flex布局内在尺寸的关键方法。它有两个主要的计算分支,取决于sizingDirection
是否与Flex的_direction
相同。
主要参数
sizingDirection
: 要计算的尺寸方向(水平或垂直)extent
: 在非计算方向上的尺寸childSize
: 用于计算子元素尺寸的函数
第一个分支: 计算主轴方向的内在尺寸
当_direction == sizingDirection
时:
dart
double totalFlex = 0.0;
double inflexibleSpace = spacing * (childCount - 1); // 考虑间距
double maxFlexFractionSoFar = 0.0;
这个分支计算:
- 累加所有非弹性元素的尺寸
- 计算所有弹性(flex>0)元素的最大弹性比例
- 最终尺寸 = 最大弹性比例 * 总弹性值 + 非弹性空间
第二个分支: 计算交叉轴方向的内在尺寸
当_direction != sizingDirection
时:
dart
final bool isHorizontal = switch (direction) {
Axis.horizontal => true,
Axis.vertical => false,
};
这个分支:
- 确定布局方向
- 定义一个
layoutChild
函数来处理子元素布局 - 使用
_computeSizes
计算最终的交叉轴尺寸
关键特点
- 考虑了
spacing
(元素间距) - 分别处理弹性和非弹性子元素
- 支持水平和垂直两个方向的计算
- 处理了无限约束的情况
使用场景
这个方法主要用于:
- 计算Row/Column的最小/最大内在宽度和高度
- 在没有明确约束时确定Flex容器的尺寸
- 处理子元素的弹性布局计算
computeMinIntrinsicWidth()
重写,调用了_getIntrinsicSize()
方法,下面类似。
computeMaxIntrinsicWidth()
同上
computeMinIntrinsicHeight()
同上
computeMaxIntrinsicHeight()
同上
_computeSizes()
主要功能:
- 计算Flex容器中所有子元素的尺寸
- 分配可用空间
- 处理基线对齐
- 返回最终的布局尺寸信息
让我们逐步分析其实现:
初始化和准备工作
dart
final double maxMainSize = _getMainSize(constraints.biggest);
final bool canFlex = maxMainSize.isFinite;
final BoxConstraints nonFlexChildConstraints = _constraintsForNonFlexChild(constraints);
- 获取主轴方向的最大可用空间
- 判断是否可以进行弹性布局(空间是否有限)
- 为非弹性子元素创建约束条件
第一次遍历 - 处理非弹性子元素
dart
int totalFlex = 0;
RenderBox? firstFlexChild;
_AscentDescent accumulatedAscentDescent = _AscentDescent.none;
_AxisSize accumulatedSize = _AxisSize._(Size(spacing * (childCount - 1), 0.0));
for (RenderBox? child = firstChild; child != null; child = childAfter(child)) {
if (canFlex && (flex = _getFlex(child)) > 0) {
totalFlex += flex;
firstFlexChild ??= child;
} else {
// 处理非弹性子元素
final _AxisSize childSize = _AxisSize.fromSize(...);
accumulatedSize += childSize;
}
}
- 计算所有flex值的总和
- 记录第一个弹性子元素
- 计算非弹性子元素的尺寸
- 累计已使用的空间
第二次遍历 - 处理弹性子元素
dart
final double flexSpace = math.max(0.0, maxMainSize - accumulatedSize.mainAxisExtent);
final double spacePerFlex = flexSpace / totalFlex;
for (RenderBox? child = firstFlexChild; child != null && totalFlex > 0; child = childAfter(child)) {
final int flex = _getFlex(child);
if (flex == 0) continue;
final double maxChildExtent = spacePerFlex * flex;
final BoxConstraints childConstraints = _constraintsForFlexChild(...);
final _AxisSize childSize = _AxisSize.fromSize(...);
accumulatedSize += childSize;
}
- 计算剩余可用空间
- 按比例分配空间给弹性子元素
- 布局每个弹性子元素
计算最终尺寸
dart
final double idealMainSize = switch (mainAxisSize) {
MainAxisSize.max when maxMainSize.isFinite => maxMainSize,
MainAxisSize.max || MainAxisSize.min => accumulatedSize.mainAxisExtent,
};
final _AxisSize constrainedSize = _AxisSize(...)
.applyConstraints(constraints, direction);
- 根据
mainAxisSize
确定理想的主轴尺寸 - 应用约束条件得到最终尺寸
特殊处理:
- 间距处理:
dart
_AxisSize accumulatedSize = _AxisSize._(Size(spacing * (childCount - 1), 0.0));
- 初始化时就考虑了子元素之间的间距
- 基线对齐:
dart
final TextBaseline? textBaseline = _isBaselineAligned ? ... : null;
- 支持文本基线对齐的特殊处理
这个方法是Flex布局的核心,它实现了类似CSS Flexbox的布局算法,包括:
- 弹性空间分配
- 子元素大小计算
- 基线对齐支持
- 间距处理
- 约束处理
computeDryLayout()
重写,调用了_computeSizes()
computeDryBaseline()
重写,调用了_computeSizes()
方法,这里逻辑略复杂