remove method
Removes a node and all its descendants from the tree.
If animate is true, the nodes will animate out.
Implementation
void remove({required TKey key, bool animate = true}) {
if (_animationStyle.effectiveEnterExit.duration == Duration.zero) {
animate = false;
}
if (!_hasKey(key)) {
return;
}
// See [insert] for the rationale: flush any deferred visible-order
// rebuild so the `_order.contains(key)` path gate below reads fresh
// state. Inside a batch a prior mutation can leave the order dirty,
// and a stale containment read picks the wrong path in both
// directions, a row made visible earlier in the batch would degrade
// to an immediate purge, and a row hidden earlier in the batch would
// animate out where nobody can see it.
_ensureVisibleOrder();
final descendants = _getDescendants(key);
final nodesToRemove = [key, ...descendants];
// Capture the parent BEFORE mutation; _removeNodesImmediate purges the
// node and releases its nid, after which _parentKeyOfKey returns null.
final parentKey = _parentKeyOfKey(key);
final affected = <TKey>{};
if (animate && _order.contains(key)) {
// Mark nodes as pending deletion so _finalizeAnimation knows to
// fully remove them (vs just hiding due to parent collapse)
for (final nodeId in nodesToRemove) {
_markPendingDeletion(nodeId);
}
// Mark all visible nodes as exiting
for (final nodeId in nodesToRemove) {
if (_order.contains(nodeId)) {
_startStandaloneExitAnimation(
nodeId,
family: TreeAnimationFamily.enterExit,
);
}
}
// Animated path: the parent's child list is not mutated until exit
// animations complete; the raw-count refresh fires from the
// standalone-tick / group-dismissed sites at purge time. But the
// parent's LIVE child count changed right here: pending-deletion
// marking is the moment liveChildCount readers go stale, so the
// parent row must refresh now as well, along with every sibling
// whose live position shifted.
affected.addAll(_siblingRefreshOrToken(parentKey));
} else {
_removeNodesImmediate(nodesToRemove);
_structureGeneration++;
// Immediate path: the parent's child-list length changed (and its
// hasChildren may have flipped), and the surviving siblings shifted
// into new positions.
affected.addAll(_siblingRefreshOrToken(parentKey));
}
_notifyStructural(affectedKeys: affected);
}