isNodeRetained method
Whether the given node is retained by the current layout, i.e. it is explicitly pinned, in the cache region, a sticky header, or a phantom-exit ghost mid-FLIP. Used by the element to decide whether an off-screen child can be evicted. O(1) (Map containsKey + Set lookup), no allocation.
Implementation
bool isNodeRetained(TKey id) {
if (_pinnedNodes.contains(id)) return true;
// Phantom-exit ghosts: retain past visible-order purge so their
// slide can finish. Removed from _phantomExitGhosts when their FLIP
// slide settles (Step 0a's criterion is FLIP-only, so a HELD
// make-room preview no longer extends this pin for the length of a
// drag), after which the next stale eviction will release the
// render box normally.
final ghosts = _phantomExitGhosts;
if (ghosts != null && ghosts.containsKey(id)) return true;
// Edge-anchor exit ghosts: retain so the parallel ghost paint pass
// can render them. The row IS in visibleNodes (its structural is
// still in the tree) but may sit outside the cache region; without
// explicit retention here, layout admission would evict the child
// and the ghost paint pass would have nothing to paint.
if (_composer.ghosts.entryFor(id) != null) return true;
final nid = _controller.nidOf(id);
if (nid < 0) return false;
if (nid < _inCacheRegionByNid.length && _inCacheRegionByNid[nid] != 0) {
return true;
}
// Mid-flight FLIP slide: the engine has a live slide entry for this
// nid (currentDelta != 0 in either axis is the externally-observable
// proxy, the engine clears entries whose composedY/X both reach 0
// and the lerp only crosses zero at completion). Retain so paint can
// continue to render the row at `structural + slideDelta` even when
// the post-mutation structural Y has moved outside the cache region
// (e.g. a re-moveTo mid-slide pushed the row far off-screen). Without
// this, stale-eviction (gated only by `hasActiveAnimations`, which
// excludes slides, see [TreeController.hasActiveAnimations] doc)
// would drop the render box mid-slide, leaving the engine ticking a
// slide for a child that no longer exists, so the row appears stuck
// / vanishes during its visible transit through the viewport. Once
// the slide settles (delta=0), this branch falls through and the
// next eviction releases the box normally.
// FLIP-only, for the reason spelled out in
// `SliverTreeElement._scheduleStaleEviction`: a held preview offset
// never decays, so the composed read retained every row a preview
// touched for the whole drag. A preview-shifted row is admitted by
// layout instead, so the cache-region check above already covers it.
//
// In fact the cache-region check above covers a mid-slide row too,
// because overreach widens that region by the composed bound. This
// clause is the belt to that braces; see the eviction gate's doc for
// the measurement.
if (_controller.getFlipSlideDeltaNid(nid) != 0.0 ||
_controller.getSlideDeltaXNid(nid) != 0.0) {
return true;
}
return _sticky.isSticky(nid);
}