indexOfNearestChildAtOffset method

  1. @override
int indexOfNearestChildAtOffset(
  1. LayoutOffset offset
)
override

Finds the index of the child at the given offset. Returns -1 if no child is found at the offset.

Implementation

@override
int indexOfNearestChildAtOffset(LayoutOffset offset) {
  // find the line at the offset
  // then find the child in that line at the offset

  if (_cache == null) {
    throw StateError('Layout has not been performed yet.');
  }
  double crossOffset = switch (layout.direction.axis) {
    LayoutAxis.horizontal => offset.dy,
    LayoutAxis.vertical => offset.dx,
  };
  double mainOffset = switch (layout.direction.axis) {
    LayoutAxis.horizontal => offset.dx,
    LayoutAxis.vertical => offset.dy,
  };

  bool reverseMain = layout.direction.reverse;
  bool reverseCross = layout.wrap == FlexWrap.wrapReverse;

  FlexLineLayoutCache? line = reverseCross
      ? _cache!.lastLine
      : _cache!.firstLine;
  while (line != null) {
    final previousLine = line.previousLine;
    final nextLine = line.nextLine;

    bool isInLine = false;

    // if reverseCross is false, then 0 is at the start cross, and N is at the end cross
    // if reverseCross is true, then 0 is at the end cross, and N is at the start cross
    if (reverseCross) {
      if (previousLine == null) {
        isInLine = true;
      } else {
        // double crossMaxOffset =
        //     line.bounds!.bottom +
        //     (previousLine.bounds!.top - line.bounds!.bottom) / 2.0;
        double crossMaxOffset = switch (layout.direction.axis) {
          LayoutAxis.horizontal =>
            line.bounds!.bottom +
                (previousLine.bounds!.top - line.bounds!.bottom) / 2.0,
          LayoutAxis.vertical =>
            line.bounds!.right +
                (previousLine.bounds!.left - line.bounds!.right) / 2.0,
        };
        if (crossOffset <= crossMaxOffset) {
          isInLine = true;
        }
      }
    } else {
      if (nextLine == null) {
        isInLine = true;
      } else {
        double crossMaxOffset = switch (layout.direction.axis) {
          LayoutAxis.horizontal =>
            line.bounds!.bottom +
                (nextLine.bounds!.top - line.bounds!.bottom) / 2.0,
          LayoutAxis.vertical =>
            line.bounds!.right +
                (nextLine.bounds!.left - line.bounds!.right) / 2.0,
        };
        if (crossOffset <= crossMaxOffset) {
          isInLine = true;
        }
      }
    }

    if (isInLine) {
      ChildLayout? child = reverseMain
          ? (line.lastChild ?? parent.lastLayoutChild)
          : line.firstChild;
      while (child != null) {
        if (child.layoutData.behavior == LayoutBehavior.absolute) {
          child = reverseMain ? child.previousSibling : child.nextSibling;
          continue;
        }
        final cache = child.layoutCache as FlexChildLayoutCache;
        if (cache.lineCache != line) {
          child = reverseMain ? child.previousSibling : child.nextSibling;
          continue;
        }
        final previousChild = child.previousSibling;
        final nextChild = child.nextSibling;
        bool hasPrevious =
            previousChild != null &&
            cache.lineCache ==
                (previousChild.layoutCache as FlexChildLayoutCache)
                    .lineCache &&
            previousChild.layoutData.behavior != LayoutBehavior.absolute;
        bool hasNext =
            nextChild != null &&
            cache.lineCache ==
                (nextChild.layoutCache as FlexChildLayoutCache).lineCache &&
            nextChild.layoutData.behavior != LayoutBehavior.absolute;
        final childBounds = child.offset & child.size;
        // for child, the divider offset is at the center of the child
        // instead of the gap between children
        /*
                center            center            center
            [      |      ]   [      |      ]   [      |      ]
          0 [  0   |   1  ] 1 [  1   |   2  ] 2 [  2   |   3  ] 3
            [      |      ]   [      |      ]   [      |      ]

            ^____box 0____^   ^____box 1____^   ^____box 2____^
        */
        if (reverseMain) {
          double mainMaxOffset = switch (layout.direction.axis) {
            LayoutAxis.horizontal => childBounds.horizontalCenter,
            LayoutAxis.vertical => childBounds.verticalCenter,
          };
          if (mainOffset <= mainMaxOffset) {
            return cache.index + 1;
          } else if (!hasPrevious) {
            return cache.index;
          }
        } else {
          double mainMaxOffset = switch (layout.direction.axis) {
            LayoutAxis.horizontal => childBounds.horizontalCenter,
            LayoutAxis.vertical => childBounds.verticalCenter,
          };
          if (mainOffset <= mainMaxOffset) {
            return cache.index;
          } else if (!hasNext) {
            return cache.index + 1;
          }
        }

        child = reverseMain ? previousChild : nextChild;
      }

      return line.lineIndex + 1000;
    }

    line = reverseCross ? previousLine : nextLine;
  }

  // technically we shouldn't get here since one of the lines should match
  return -1;
}