AspectRatio widget 是用来约束宽高比例的,如果你需要 widget 必须要有一定的宽高比,这个组件就很有用。
但 AspectRatio 并不总是如你所愿,我一度想直接设置宽高达到同样的目的,但当你了解了它的逻辑之后,发现这样做并不明智,因为即使是直接设置高度,这些逻辑也是少不了的。
AspectRatio 需要父容器至少 width 或 height 是有边界的。
想想也是合理的。如果约束是没有边界的,而 AspectRatio 本身又没有宽高属性,AspectRatio 就无法设置宽高,只能都是无限,就和宽高有比例这个要求相矛盾。
AspectRatio 的尺寸的计算逻辑
- 如果父元素的宽高都没有边界,抛异常。
- 如果收到的是 tight 约束,那么相当于 AspectRatio 无效,不会起到任何作用,只会透传约束。
- 优先以宽度为准,按比例设置高度。如果宽度无限,以高度为准按比例设置宽度。
- 无论是高度还是宽度都必须在约束的范围内。
AspectRatio 的代码逻辑
决定宽高的代码
Size _applyAspectRatio(BoxConstraints constraints) { assert(constraints.debugAssertIsValid()); assert(() { if (!constraints.hasBoundedWidth && !constraints.hasBoundedHeight) { throw FlutterError( '$runtimeType has unbounded constraints.\n' 'This $runtimeType was given an aspect ratio of $aspectRatio but was given ' 'both unbounded width and unbounded height constraints. Because both ' "constraints were unbounded, this render object doesn't know how much " 'size to consume.', ); } return true; }()); if (constraints.isTight) { return constraints.smallest; } double width = constraints.maxWidth; double height; // We default to picking the height based on the width, but if the width // would be infinite, that's not sensible so we try to infer the height // from the width. if (width.isFinite) { height = width / _aspectRatio; } else { height = constraints.maxHeight; width = height * _aspectRatio; } // Similar to RenderImage, we iteratively attempt to fit within the given // constraints while maintaining the given aspect ratio. The order of // applying the constraints is also biased towards inferring the height // from the width. if (width > constraints.maxWidth) { width = constraints.maxWidth; height = width / _aspectRatio; } if (height > constraints.maxHeight) { height = constraints.maxHeight; width = height * _aspectRatio; } if (width < constraints.minWidth) { width = constraints.minWidth; height = width / _aspectRatio; } if (height < constraints.minHeight) { height = constraints.minHeight; width = height * _aspectRatio; } return constraints.constrain(Size(width, height)); } 复制代码
定好自己的宽高后,把 child 设置成和自己一样宽高
@override void performLayout() { size = computeDryLayout(constraints); if (child != null) { child!.layout(BoxConstraints.tight(size)); } } 复制代码
AspectRatio 的 renderObject 继承自 RenderProxyBox。只可以有一个child,没有 alignment 属性,不能摆放 child的位置。
AspectRatio 的 aspectRatio 参数要求是 大于0 的 double ,但是一般都是用分数的形式来写,这样可读性会更好些。
AspectRatio( aspectRatio: 1 / 2, );