updateChild method
Reconciles child against newSeed at slot.
When an existing child is reconciled against an identical newSeed
(identical(child.seed, newSeed) — the identical-skip fast path, ported
from Flutter's Element.updateChild), the child is returned untouched: no
update, no rebuild, no subtree cascade. A const-canonicalized seed
or a deliberately reused instance therefore prunes its whole subtree at
reconcile time. The skip is identity-only by construction — it never
consults Seed.operator==, so seeds remain free to define value
equality (e.g. for wire diffing) without changing reconcile semantics.
The skip is reconciliation's concern only: update keeps its
force-rebuild semantics, so a direct branch.update(sameInstance) still
rebuilds. A provider whose value changed but whose child instance is
reused invalidates its dependents through dependencyChanged
independently of this skip; they land in the owner dirty set and rebuild
when TreeOwner.flush drains them.
Deferred obligation: Flutter still updates a skipped child's slot on
the fast path (updateSlotForChild). Branch stores no slot — position
lives only in the parent's child list — so there is no slot-update branch
here yet. The day render branches grow slots, the skip must update the
slot before returning.
Implementation
Branch? updateChild(Branch? child, Seed? newSeed, Object? slot) {
if (newSeed == null) {
child?.unmount();
return null;
}
if (child != null) {
// Identical-skip fast path: an identical config skips the rebuild
// entirely.
if (identical(child._seed, newSeed)) {
return child;
}
if (Seed.canUpdate(child._seed, newSeed)) {
child.update(newSeed);
return child;
}
child.unmount();
}
final branch = newSeed.createBranch();
branch.mount(this, slot);
return branch;
}