reassemble method
Invokes State.reassemble on every live retained state and marks every
live element dirty so the next buildScope re-runs its build().
This is hot reload's second step: reloadSources swaps method bodies but
leaves the element tree untouched, so nothing re-executes an edited
build() until something marks it dirty. No State is recreated and no
initState re-runs — retained states get State.reassemble instead, in
document order (parents before children) so a child re-derives against an
already-refreshed parent. Every callback is attempted, the tree is marked
whether or not any of them fail, and the first failure is rethrown once
the pass is complete: a broken hook degrades the reload rather than
aborting it.
Deliberate design decision: every registered element is marked, not only
the root. Reconciliation currently has no identical-widget short-circuit,
so marking the root alone would happen to cascade through the whole tree
— but that is a property of reconciliation, not a guarantee this method
should depend on. Marking directly keeps reassemble correct if such a
short-circuit is ever added. scheduleBuild's generation-stamped
reservations still collapse the marks with the parent cascade, so each
element rebuilds exactly once.
Safe with nothing mounted, after dispose, and from inside a build():
an in-build call drains in a later batch of the same buildScope pass
rather than re-entering it.
Implementation
void reassemble() {
if (_disposed) {
return;
}
// Snapshot first: `scheduleBuild` requests a frame through `_onFrame`, and
// a host callback may mount elements before this loop finishes.
final registered = _depths.keys.toList(growable: false);
final failures = FirstErrorRecorder();
for (final element in _reassembleTargets(registered)) {
failures.attempt(element.state.reassemble);
}
for (final element in registered) {
scheduleBuild(element);
}
failures.rethrowFirst();
}