box static method
- required Iterable<
LayoutItem> items, - required LayoutConstraints constraints,
- required double? targetWidth,
- required double? targetHeight,
- required EdgeInsets padding,
- required Anchor? alignment,
Sizes a region to targetWidth/targetHeight - or to its largest item
plus padding where either is null - then places every item inside it
at alignment.
Example (no alignment)
Let's say you have a 100x60 region with 10 of padding and no alignment, holding one resizable item.
+----------------------------------------+
| |
| +--------------------------------+ |
| | item | |
| +--------------------------------+ |
| |
+----------------------------------------+
0 10 90 100
The steps are as follows:
- Narrow the constraints. The padding leaves an 80x40 interior, and nothing loosens it, so its minimum carries through.
- Measure every item against that. Forced to fill, the item reports 80x40.
- Size the region. With no target it shrink-wraps to the largest item plus padding, landing on 100x60.
- Place every item on the padded origin, (10, 10).
Example (alignment)
Now the same region, aligned at (0.5, 0.5), holding one 20x20 item.
+----------------------------------------+
| |
| +------+ |
| | item | |
| +------+ |
| |
+----------------------------------------+
0 40 60 100
The steps are as follows:
- Narrow the constraints. The padding leaves an 80x40 interior, and aligning loosens it, so the item may come back smaller.
- Measure every item against that. The item reports 20x20.
- Size the region. Aligning asks for room to align within, so each bounded axis is taken whole, landing on 100x60.
- Place every item. That leaves 60x20 of room inside the padding, and half of it puts the item at (40, 20).
Implementation
static Vector2 box({
required Iterable<LayoutItem> items,
required LayoutConstraints constraints,
required double? targetWidth,
required double? targetHeight,
required EdgeInsets padding,
required Anchor? alignment,
}) {
final LayoutConstraints region;
if (targetWidth != null || targetHeight != null) {
region = LayoutConstraints(
min: .new(targetWidth ?? 0, targetHeight ?? 0),
max: .new(targetWidth ?? double.infinity, targetHeight ?? double.infinity),
).enforce(constraints);
} else {
region = constraints;
}
var itemConstraints = region;
if (padding != .zero) {
itemConstraints = itemConstraints.deflate(padding);
}
if (alignment != null) {
itemConstraints = itemConstraints.loosen();
}
// Measure. An item reports a size in its own space, so it is measured
// against constraints carried into that space and compared on the extent
// that size occupies back out here.
final largest = MVector2.zero();
final extent = MVector2.zero();
for (final item in items) {
item.layout(itemConstraints.descale(item.scale));
extent
..setFrom(item.size)
..multiply(item.scale)
..absolute();
largest.max(extent);
}
// Size. An alignment asks for room to align within, so it takes every
// bounded axis whole; an unbounded one has no room to ask for. [region] is
// already tight on any targeted axis, so a target still wins either way.
final fill = alignment != null;
final size = region.constrain(
fill && region.hasBoundedWidth ? region.max.x : largest.x + padding.horizontal,
fill && region.hasBoundedHeight ? region.max.y : largest.y + padding.vertical,
);
// Place. Nothing was kept from the measure pass: [LayoutItem] guarantees
// an item still reports the size it just measured at. An unaligned item
// multiplies the leftover room by zero, landing on the padded origin.
final anchor = alignment ?? .topLeft;
final innerWidth = size.x - padding.horizontal;
final innerHeight = size.y - padding.vertical;
for (final item in items) {
extent
..setFrom(item.size)
..multiply(item.scale)
..absolute();
place(
item,
.new(
padding.left + (innerWidth - extent.x) * anchor.x,
padding.top + (innerHeight - extent.y) * anchor.y,
),
);
}
return size;
}