indexOfNearestChildAtOffset method
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;
}