splitRect function

List<Rect> splitRect(
  1. Rect area,
  2. List<Constraint> constraints,
  3. LayoutDirection direction, {
  4. MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
})

A Viewport wraps a parent buffer, translating and clipping drawing operations to a relative local coordinate space within bounds.

Implementation

List<Rect> splitRect(
  Rect area,
  List<Constraint> constraints,
  LayoutDirection direction, {
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
}) {
  final clampedArea = Rect(
    area.x,
    area.y,
    max(0, area.width),
    max(0, area.height),
  );
  final totalSize = direction == LayoutDirection.horizontal
      ? clampedArea.width
      : clampedArea.height;
  final sizes = List<int>.filled(constraints.length, 0);

  var usedSize = 0;
  var totalFlex = 0;
  final flexIndices = <int>[];

  // 1. Calculate fixed sizes, percentages, and min constraints
  for (var i = 0; i < constraints.length; i++) {
    final c = constraints[i];
    if (c is LengthConstraint) {
      sizes[i] = c.length;
      usedSize += c.length;
    } else if (c is PercentageConstraint) {
      final size = (totalSize * c.percentage / 100).round();
      sizes[i] = size;
      usedSize += size;
    } else if (c is FlexConstraint) {
      totalFlex += c.flex;
      flexIndices.add(i);
    } else if (c is MinMaxConstraint) {
      sizes[i] = c.min;
      usedSize += c.min;
    }
  }

  // 2. Distribute remaining space among Flex constraints
  if (totalFlex > 0 && usedSize < totalSize) {
    final remaining = totalSize - usedSize;
    var distributed = 0;
    for (var j = 0; j < flexIndices.length; j++) {
      final idx = flexIndices[j];
      final flex = (constraints[idx] as FlexConstraint).flex;
      final size = j == flexIndices.length - 1
          ? remaining - distributed
          : (remaining * flex / totalFlex).floor();
      sizes[idx] = size;
      distributed += size;
    }
    usedSize += distributed;
  }

  // 3. (Removed: Scale down if used size exceeds total size)
  // We no longer forcefully scale down rigid constraints. This allows layouts
  // to correctly overflow their bounds so the rendering engine can detect it
  // and display a visual warning, rather than silently hiding elements.

  // 4. Respect Max Constraints on MinMax
  for (var i = 0; i < constraints.length; i++) {
    final c = constraints[i];
    if (c is MinMaxConstraint) {
      sizes[i] = sizes[i].clamp(c.min, c.max);
    }
    sizes[i] = max(0, sizes[i]);
  }

  // 5. Construct child Rects
  final rects = <Rect>[];
  final N = sizes.length;
  final remaining = totalSize - usedSize;

  if (N == 0) return rects;

  final sumOfSizes = List<int>.filled(N + 1, 0);
  for (var i = 0; i < N; i++) {
    sumOfSizes[i + 1] = sumOfSizes[i] + sizes[i];
  }

  int getChildOffset(int i) {
    if (remaining <= 0) {
      return switch (mainAxisAlignment) {
        MainAxisAlignment.start => sumOfSizes[i],
        MainAxisAlignment.end => remaining + sumOfSizes[i],
        MainAxisAlignment.center => (remaining ~/ 2) + sumOfSizes[i],
        _ =>
          sumOfSizes[i], // Fallback to start for spaced alignments when overflowing
      };
    }
    return switch (mainAxisAlignment) {
      MainAxisAlignment.start => sumOfSizes[i],
      MainAxisAlignment.end => remaining + sumOfSizes[i],
      MainAxisAlignment.center => (remaining ~/ 2) + sumOfSizes[i],
      MainAxisAlignment.spaceBetween =>
        N <= 1 ? sumOfSizes[i] : ((remaining * i) ~/ (N - 1)) + sumOfSizes[i],
      MainAxisAlignment.spaceAround =>
        (remaining * (i * 2 + 1) / (N * 2)).floor() + sumOfSizes[i],
      MainAxisAlignment.spaceEvenly =>
        (remaining * (i + 1) / (N + 1)).floor() + sumOfSizes[i],
    };
  }

  for (var i = 0; i < N; i++) {
    final size = sizes[i];
    final offset = getChildOffset(i);
    if (direction == LayoutDirection.horizontal) {
      rects.add(
        Rect(clampedArea.x + offset, clampedArea.y, size, clampedArea.height),
      );
    } else {
      rects.add(
        Rect(clampedArea.x, clampedArea.y + offset, clampedArea.width, size),
      );
    }
  }

  return rects;
}