verticalOffsetForBulkChange<T> static method

double? verticalOffsetForBulkChange<T>({
  1. required List<FlatNode<T>> oldList,
  2. required List<FlatNode<T>> newList,
  3. required double currentOffset,
  4. required double itemExtent,
  5. required double topPadding,
  6. required double newContentHeight,
  7. required double viewportHeight,
})

New vertical offset after a bulk list change (e.g. expandAll/collapseAll), anchoring to the top node — or its nearest surviving ancestor if it was removed. Null when no adjustment is needed.

Implementation

static double? verticalOffsetForBulkChange<T>({
  required List<FlatNode<T>> oldList,
  required List<FlatNode<T>> newList,
  required double currentOffset,
  required double itemExtent,
  required double topPadding,
  required double newContentHeight,
  required double viewportHeight,
}) {
  if (identical(oldList, newList)) return null;
  if (oldList.isEmpty || newList.isEmpty) return null;

  final topIndex = ((currentOffset - topPadding) / itemExtent)
      .floor()
      .clamp(0, oldList.length - 1);

  var anchorPixelOffset =
      currentOffset - (topPadding + topIndex * itemExtent);

  int newIndex = -1;
  for (int i = topIndex; i >= 0; i--) {
    newIndex = newList.indexWhere((fn) => fn.node.id == oldList[i].node.id);
    if (newIndex >= 0) {
      if (i != topIndex) anchorPixelOffset = 0.0;
      break;
    }
  }
  if (newIndex < 0) return null;

  final targetOffset = topPadding + newIndex * itemExtent + anchorPixelOffset;
  final newMaxExtent =
      (newContentHeight - viewportHeight).clamp(0.0, double.infinity);
  final clamped = targetOffset.clamp(0.0, newMaxExtent);

  return (clamped - currentOffset).abs() > 0.5 ? clamped : null;
}