constraintGrid function

List<Widget> constraintGrid({
  1. required ConstraintId id,
  2. required ConstraintAlign left,
  3. required ConstraintAlign top,
  4. required int itemCount,
  5. required int columnCount,
  6. double? itemWidth,
  7. double? itemHeight,
  8. double? itemSize,
  9. Size itemSizeBuilder(
    1. int index,
    2. int rowIndex,
    3. int columnIndex
    )?,
  10. required Widget itemBuilder(
    1. int index,
    2. int rowIndex,
    3. int columnIndex
    ),
  11. EdgeInsets itemMarginBuilder(
    1. int index,
    2. int rowIndex,
    3. int columnIndex
    )?,
  12. int itemSpanBuilder(
    1. int index
    )?,
  13. EdgeInsets margin = EdgeInsets.zero,
  14. CLVisibility visibility = visible,
  15. Offset translate = Offset.zero,
  16. bool translateConstraint = false,
  17. int? zIndex,
})

For list、grid、staggered grid

Implementation

List<Widget> constraintGrid({
  required ConstraintId id,
  required ConstraintAlign left,
  required ConstraintAlign top,
  required int itemCount,
  required int columnCount,
  double? itemWidth,
  double? itemHeight,
  double? itemSize,
  Size Function(int index, int rowIndex, int columnIndex)? itemSizeBuilder,
  required Widget Function(int index, int rowIndex, int columnIndex)
      itemBuilder,
  EdgeInsets Function(int index, int rowIndex, int columnIndex)?
      itemMarginBuilder,
  int Function(int index)? itemSpanBuilder,
  EdgeInsets margin = EdgeInsets.zero,
  CLVisibility visibility = visible,
  Offset translate = Offset.zero,
  bool translateConstraint = false,
  int? zIndex,
}) {
  assert(itemCount > 0);
  assert(columnCount > 0);
  assert(itemWidth == null || (itemWidth >= 0 || itemWidth != matchConstraint));
  assert(
      itemHeight == null || (itemHeight >= 0 || itemHeight != matchConstraint));
  assert(itemSize == null || (itemSize >= 0 || itemSize != matchConstraint));
  if (itemSize != null) {
    itemWidth = itemSize;
    itemHeight = itemSize;
  }
  assert((itemSizeBuilder == null && itemWidth != null && itemHeight != null) ||
      (itemSizeBuilder != null && itemWidth == null && itemHeight == null));
  List<Widget> widgets = [];
  ConstraintAlign leftAnchor = left;
  ConstraintAlign topAnchor = top;

  EdgeInsets leftMargin = EdgeInsets.only(
    left: margin.left,
  );
  EdgeInsets topMargin = EdgeInsets.only(
    top: margin.top,
  );

  List<ConstraintId> allChildIds = [];
  List<ConstraintId> leftChildIds = [];
  List<ConstraintId> topChildIds = [];
  List<ConstraintId> rightChildIds = [];
  List<ConstraintId> bottomChildIds = [];
  int totalAvailableSpanCount = (itemCount / columnCount).ceil() * columnCount;
  int currentRowIndex = -1;
  int currentRowUsedSpanCount = columnCount + 1;
  int totalUsedSpanCount = 0;
  late int currentRowBarrierCount;
  List<ConstraintId?> currentSpanSlot = List.filled(columnCount + 1, null);
  for (int i = 0; i < itemCount; i++) {
    ConstraintId itemId = ConstraintId(id.id + '_grid_item_$i');
    allChildIds.add(itemId);

    int itemSpan = itemSpanBuilder?.call(i) ?? 1;
    assert(itemSpan >= 1 && itemSpan <= columnCount);
    currentRowUsedSpanCount += itemSpan;
    totalUsedSpanCount += itemSpan;

    late EdgeInsets childMargin;

    /// New row start
    if (currentRowUsedSpanCount > columnCount) {
      currentRowIndex++;
      currentRowUsedSpanCount = itemSpan;
      currentRowBarrierCount = 0;
      if (i > 0) {
        if (!rightChildIds.contains(allChildIds[i - 1])) {
          /// Last column
          rightChildIds.add(allChildIds[i - 1]);
        }
      } else {
        if (itemSpan == columnCount) {
          /// Last column
          rightChildIds.add(itemId);
        }
      }

      /// First column
      leftAnchor = left;
      leftChildIds.add(itemId);
      childMargin = (itemMarginBuilder?.call(
                  i, currentRowIndex, currentRowUsedSpanCount - 1) ??
              EdgeInsets.zero)
          .add(leftMargin) as EdgeInsets;
    } else {
      childMargin = itemMarginBuilder?.call(
              i, currentRowIndex, currentRowUsedSpanCount - 1) ??
          EdgeInsets.zero;
    }

    // First row
    if (currentRowIndex == 0) {
      childMargin = childMargin.add(topMargin) as EdgeInsets;
      topChildIds.add(itemId);
    }

    // Last row
    if (totalAvailableSpanCount - totalUsedSpanCount < columnCount) {
      bottomChildIds.add(itemId);
    }

    if (currentRowIndex > 0) {
      if (itemSpan == 1) {
        topAnchor = currentSpanSlot[currentRowUsedSpanCount]!.bottom;
      } else {
        List<ConstraintId> referencedIds = [];
        for (int i = 0; i < itemSpan; i++) {
          ConstraintId id = currentSpanSlot[currentRowUsedSpanCount - i]!;
          if (!referencedIds.contains(id)) {
            referencedIds.add(id);
          }
        }
        ConstraintId rowBarrierId = ConstraintId(id.id +
            '_row_${currentRowIndex}_bottom_barrier_$currentRowBarrierCount');
        Barrier rowBottomBarrier = Barrier(
          id: rowBarrierId,
          direction: BarrierDirection.bottom,
          referencedIds: referencedIds,
        );
        widgets.add(rowBottomBarrier);
        topAnchor = rowBarrierId.bottom;
        currentRowBarrierCount++;
      }
    }

    Widget widget =
        itemBuilder(i, currentRowIndex, currentRowUsedSpanCount - 1);
    Size? itemSize =
        itemSizeBuilder?.call(i, currentRowIndex, currentRowUsedSpanCount - 1);
    double width = itemWidth ?? itemSize!.width;
    double height = itemHeight ?? itemSize!.height;

    widgets.add(Constrained(
      child: widget,
      constraint: Constraint(
        id: itemId,
        width: width,
        height: height,
        left: width == matchParent ? null : leftAnchor,
        top: height == matchParent ? null : topAnchor,
        zIndex: zIndex,
        translate: translate,
        visibility: visibility,
        margin: childMargin,
        goneMargin: childMargin,
      ),
    ));

    leftAnchor = itemId.right;
    for (int i = 0; i < itemSpan; i++) {
      currentSpanSlot[currentRowUsedSpanCount - i] = itemId;
    }
  }

  if (!rightChildIds.contains(allChildIds.last)) {
    rightChildIds.add(allChildIds.last);
  }

  Barrier leftBarrier = Barrier(
    id: ConstraintId(id.id + '_left_barrier'),
    direction: BarrierDirection.left,
    referencedIds: leftChildIds,
  );

  Barrier topBarrier = Barrier(
    id: ConstraintId(id.id + '_top_barrier'),
    direction: BarrierDirection.top,
    referencedIds: topChildIds,
  );

  Barrier rightBarrier = Barrier(
    id: ConstraintId(id.id + '_right_barrier'),
    direction: BarrierDirection.right,
    referencedIds: rightChildIds,
  );

  Barrier bottomBarrier = Barrier(
    id: ConstraintId(id.id + '_bottom_barrier'),
    direction: BarrierDirection.bottom,
    referencedIds: bottomChildIds,
  );

  widgets.add(leftBarrier);
  widgets.add(topBarrier);
  widgets.add(rightBarrier);
  widgets.add(bottomBarrier);

  widgets.add(const SizedBox().applyConstraint(
    id: id,
    size: matchConstraint,
    left: leftBarrier.id.left,
    top: topBarrier.id.top,
    right: rightBarrier.id.right,
    bottom: bottomBarrier.id.bottom,
    zIndex: -1,
    translate: translate,
    translateConstraint: translateConstraint,
    visibility: invisible,
  ));

  return widgets;
}