build method
Initialize a Notifier.
It is safe to use Ref.watch or Ref.listen inside this method.
If a dependency of this Notifier (when using Ref.watch) changes,
then build will be re-executed. On the other hand, the Notifier
will not be recreated. Its instance will be preserved between
executions of build.
If this method throws, reading this provider will rethrow the error.
Implementation
@override
AutoShieldStatus build() {
_disposed = false;
_generation++;
_deniedThisSession = false;
_quietAtTransparentZat = null;
ref.onDispose(() => _disposed = true);
// Identity keys the loop (same rule as the whole live graph). Everything
// else is a LISTEN, not a watch — a policy/balance change must evaluate,
// not rebuild (a rebuild would re-arm a denied latch).
final session = ref.watch(walletSessionProvider);
// Every refreshed snapshot is an evaluation point: the wallet-active sync
// edges (reachedTip / becameSpendable) invalidate it, so arrivals are
// seen exactly when the balance is. The post-attempt invalidate below
// re-evaluates once more — by then the proposed UTXOs are consumed and
// `proposeShield` quietly returns null, so there is no hot loop.
ref.listen(walletSnapshotProvider, (_, next) {
if (next.hasValue) _maybeAttempt();
});
// Power-save lifting and the switch loading/turning ON are the two other
// "now it may fire" edges.
ref.listen(walletPowerSaveActiveProvider, (_, active) {
if (!active) _maybeAttempt();
});
ref.listen(walletAutoShieldEnabledProvider, (prev, next) {
// TRANSITION-keyed on SETTLED values (wrap security MINOR-3): a
// provider rebuild re-emits the current value through loading-with-
// previous states — a value-keyed arm would re-arm a denied latch (and
// re-prompt a denying host) with no user gesture. Only a settled value
// that DIFFERS from the previous one is a user-driven flip; the cold
// load (prev carries no value) counts as a flip in its own direction.
if (!next.hasValue || next.isLoading) return;
final on = next.value == true;
final wasOn = prev?.value == true;
if (on && !wasOn) {
// Turning the switch ON is the user's explicit "run automation" —
// it RE-ARMS a host-denied loop (the denial latch guards against
// prompt SPAM, not against a deliberate restart; review m6) and
// re-evaluates.
_deniedThisSession = false;
_maybeAttempt();
} else if (!on && (prev == null || wasOn)) {
// A deliberate OFF is the expert's hold-transparent choice — a
// lingering failure/denial cue would contradict the explicit opt-out
// (review m5); the standing transparent note still explains the
// visibility.
_setStatus(AutoShieldStatus.idle, _generation);
}
});
if (session != null) {
// Cold-start evaluation (a wallet opened with an existing transparent
// balance must not wait for the first sync edge). Deferred: build()
// must not write state.
Future<void>.microtask(_maybeAttempt);
}
return AutoShieldStatus.idle;
}