box static method

Vector2 box({
  1. required Iterable<LayoutItem> items,
  2. required LayoutConstraints constraints,
  3. required double? targetWidth,
  4. required double? targetHeight,
  5. required EdgeInsets padding,
  6. 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:

  1. Narrow the constraints. The padding leaves an 80x40 interior, and nothing loosens it, so its minimum carries through.
  2. Measure every item against that. Forced to fill, the item reports 80x40.
  3. Size the region. With no target it shrink-wraps to the largest item plus padding, landing on 100x60.
  4. 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:

  1. Narrow the constraints. The padding leaves an 80x40 interior, and aligning loosens it, so the item may come back smaller.
  2. Measure every item against that. The item reports 20x20.
  3. Size the region. Aligning asks for room to align within, so each bounded axis is taken whole, landing on 100x60.
  4. 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;
}