computeStickyHeaders method

void computeStickyHeaders({
  1. required double scrollOffset,
  2. required double overlap,
  3. required List<TKey> visibleNodes,
  4. required Float64List nodeOffsetsByNid,
  5. required Float64List nodeExtentsByNid,
  6. required FindFirstVisibleIndex findFirstVisibleIndex,
})

Computes sticky headers based on scroll position, populating headers and the per-nid lookup. Called after Pass 2 when actual extents and offsets are available. overlap is constraints.overlap — the number of pixels at the top covered by a preceding pinned sliver (e.g. PinnedHeaderSliver).

Implementation

void computeStickyHeaders({
  required double scrollOffset,
  required double overlap,
  required List<TKey> visibleNodes,
  required Float64List nodeOffsetsByNid,
  required Float64List nodeExtentsByNid,
  required FindFirstVisibleIndex findFirstVisibleIndex,
}) {
  // Null out prior-layout sticky entries before recomputing. Iterate
  // [_writtenStickyNids] (the nids we wrote LAST frame) instead of
  // resolving keys back to nids — a key that was freed since last
  // layout (immediate-purge removal) would yield `noNid`, leaving the
  // stale entry to leak stickiness onto whichever fresh key recycles
  // the nid. The nid handle is stable until reallocation, so clearing
  // through it is correct even when the original occupant is gone.
  for (int i = 0; i < _writtenStickyNidsLen; i++) {
    final nid = _writtenStickyNids[i];
    if (nid >= 0 && nid < _stickyByNid.length) {
      _stickyByNid[nid] = null;
    }
  }
  _writtenStickyNidsLen = 0;
  _stickyHeaders.clear();

  double? parentPinnedY;
  _forEachStickyCandidate(
    scrollOffset: scrollOffset,
    overlap: overlap,
    visibleNodes: visibleNodes,
    nodeOffsetsByNid: nodeOffsetsByNid,
    nodeExtentsByNid: nodeExtentsByNid,
    findFirstVisibleIndex: findFirstVisibleIndex,
    onCandidate: (candidateId, pinnedY, extent, stackTop) {
      // Deeper headers can slide behind parent, but must never go above
      // parent TOP.
      if (parentPinnedY != null) {
        pinnedY = math.max(parentPinnedY!, pinnedY);
        if (pinnedY + extent <= stackTop) return false;
      }

      final indent = _controller.getIndent(candidateId);
      final info = StickyHeaderInfo<TKey>(
        nodeId: candidateId,
        pinnedY: pinnedY,
        extent: extent,
        indent: indent,
      );
      _stickyHeaders.add(info);
      final nid = _controller.nidOf(candidateId);
      _stickyByNid[nid] = info;
      if (_writtenStickyNidsLen == _writtenStickyNids.length) {
        final grown = Int32List(_writtenStickyNids.length * 2);
        grown.setRange(0, _writtenStickyNidsLen, _writtenStickyNids);
        _writtenStickyNids = grown;
      }
      _writtenStickyNids[_writtenStickyNidsLen++] = nid;

      parentPinnedY = pinnedY;
      return true;
    },
  );
  _lastStickyScrollOffset = scrollOffset;
}