splitRect function

List<Rect> splitRect(
  1. Rect area,
  2. List<Constraint> constraints,
  3. LayoutDirection direction
)

Splits a Rect area into multiple sub-rectangles according to layout constraints.

Implementation

List<Rect> splitRect(
  Rect area,
  List<Constraint> constraints,
  LayoutDirection direction,
) {
  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. Scale down if used size exceeds total size
  if (usedSize > totalSize) {
    var currentSum = 0;
    for (var i = 0; i < sizes.length; i++) {
      sizes[i] = (sizes[i] * totalSize / usedSize).floor();
      currentSum += sizes[i];
    }
    if (currentSum < totalSize) {
      for (var i = sizes.length - 1; i >= 0; i--) {
        if (sizes[i] > 0) {
          sizes[i] += (totalSize - currentSum);
          break;
        }
      }
    }
  }

  // 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>[];
  var offset = 0;
  for (var i = 0; i < sizes.length; i++) {
    final size = sizes[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),
      );
    }
    offset += size;
  }

  return rects;
}