animateSlideFromOffsets method

void animateSlideFromOffsets(
  1. Map<TKey, ({double x, double y})> priorOffsets,
  2. Map<TKey, ({double x, double y})> currentOffsets, {
  3. Duration duration = const Duration(milliseconds: 220),
  4. Curve curve = Curves.easeOutCubic,
  5. double maxSlideDistance = double.infinity,
})

Starts a FLIP slide animation for every visible node whose position in scroll-space changed between priorOffsets (pre-mutation) and currentOffsets (post-mutation). Produce both with RenderSliverTree.snapshotVisibleOffsets — the first before the structural mutation, the second from inside a WidgetsBinding.addPostFrameCallback after the mutation's layout has run.

A node present in both maps with priorOffsets[key] != currentOffsets[key] receives a new SlideAnimation with startDelta = prior - current. A node only in one map is ignored (it was either added or removed and has its own enter/exit animation for that). A zero delta installs no entry.

Composes with an in-flight slide: if key already has an entry, its startDelta is replaced with currentDelta_old + (prior - current) and progress is reset to 0.0. This preserves the currently rendered position as the new animation's starting point (no visual jump).

Slide is paint-only: it does not fire the structural-change channel, does not touch layout, and is not counted in hasActiveAnimations. It fires on the animation-listener channel on every tick and on completion; see the slide tick handler for ordering.

Safe to invoke from inside RenderObject.performLayout: the slide is driven by a Ticker whose first callback fires on the next vsync (in SchedulerPhase.transientCallbacks). No listeners fire synchronously from this call, so there is no path that reaches markNeedsLayout/markNeedsPaint on a sliver currently being laid out. The per-entry currentDelta is seeded to startDelta, so the paint pass of the same frame reads the pre-mutation position.

Implementation

void animateSlideFromOffsets(
  Map<TKey, ({double y, double x})> priorOffsets,
  Map<TKey, ({double y, double x})> currentOffsets, {
  Duration duration = const Duration(milliseconds: 220),
  Curve curve = Curves.easeOutCubic,
  double maxSlideDistance = double.infinity,
}) => _slide.animateFromOffsets(
  priorOffsets,
  currentOffsets,
  duration: duration,
  curve: curve,
  maxSlideDistance: maxSlideDistance,
  structuralAnimationsDisabled: animationDuration == Duration.zero,
);