The universal reactive state protocol in the Bloom framework.
Implemented by all state-bearing entities:
- BloomController (and RawBloomController, imperative state producer)
- BloomSnapshot (Immutable point-in-time state container)
- RawBloomTerminal and RawComputedTerminal (Reactive consumer sinks)
- BloomCoupler (Dynamic socket relay)
- Computation (Class-based derivation component)
Note that Bloom itself does not implement BloomState directly; instead, it exposes an untracked live view via BloomUntracked.state or scoped tracking via BloomVia.state.
Guarantees consistent state interrogation (BloomState.phase, BloomState.data, BloomState.hasData, BloomState.isEmpty, BloomState.emptyReason, BloomState.hasError, BloomState.error, BloomState.stackTrace) and declarative pattern matching via exhaustive Dart 3 switch on BloomState.phase.
System Architectural Flow
1. 3-State Finite State Machine
┌────────────────────────────────────────┐
│ BloomState<T> 3-State State Machine │
└────────────────────────────────────────┘
│ (Initial uninitialized / late)
▼
┌──────────────┐ Value Resolved ┌──────────────┐
│ EMPTY │ ────────────────────────────> │ DATA │ (hasData == true)
│ (isEmpty) │ <──────────────────────────── │ (payload:T) │
└──────┬───────┘ .empty([reason]) └──────┬───────┘
│ │
│ .setError() .setError() │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ ERROR │ (hasError == true)
│ (payload: Object, [StackTrace]) │
└─────────────────────────────────────────────────────────────┘
2. Explicit Dependency Registration vs. Raw Reads
[computed / Effect.via Scope]
│ (Injected BloomVia parameter)
▼
via(sourceBloom) ──> [Registers Source Edge & Returns Payload T]
[Outside Derivation Scope / Raw Read]
│
untracked(sourceBloom) ──> [Pure Direct Field Dereference (Zero Tracking Overhead)]
Key Invariants & Architectural Benefits
- Zero-Overhead Raw Reads & Explicit Tracking: Reading via untracked or BloomUntracked.state directly on
a node is a pure field dereference with zero tracking overhead and no dynamic tracking stack penalty.
Reactive derivations explicitly declare dependencies via the injected BloomVia parameter (
via(node)). - 3-State Exclusivity: A BloomState entity is strictly in one of three mutually exclusive states:
- Data (BloomState.hasData == true): Contains a valid payload of type
T(includingnullfor nullableT?). - Empty (BloomState.isEmpty == true): Uninitialized or dormant state with optional BloomState.emptyReason.
- Error (BloomState.hasError == true): Failure state encapsulating BloomState.error and optional BloomState.stackTrace.
- Data (BloomState.hasData == true): Contains a valid payload of type
- Sound Fail-Fast Extraction: Reading BloomState.data while in Empty or Error state throws BloomSnapshotExtractionException.
Use BloomState.dataOrNull, BloomState.dataOrElse, or native Dart 3
switch (state.phase)for safe exhaustive pattern matching.
Warning (Anti-Pattern Prevention):
- Do NOT read untracked values directly inside a computed or
Effect.viascope without wrapping invia(...), as callinguntracked(node)performs a pure untracked read. - Do NOT read BloomState.data without checking BloomState.hasData or handling fallback via BloomState.dataOrNull / BloomState.dataOrElse.
BAD:
// [Anti-Pattern] Reading untracked without via() bypasses dependency registration
final badComputed = computed((via) {
return untracked(count.bloom) * 2; // count is NOT tracked!
});
GOOD:
// [Recommended] Explicitly register dependencies through via()
final goodComputed = computed((via) {
return via(count.bloom) * 2; // Dynamically and explicitly registered
});
Examples
final count = blooming(0);
final doubled = computed((via) => via(count.bloom) * 2);
print(untracked(doubled)); // 0
count.data = 5;
print(untracked(doubled)); // 10
Properties
- data → T
-
Extracts the underlying data synchronously from this node or state.
no setter
- dataOrNull → T?
-
Safely extracts the underlying data, returning
nullif not in a hasData state.no setter - emptyReason → Object?
-
The optional reason explaining why this state is empty, or
nullif unspecified or not empty.no setter - error → Object
-
Extracts the underlying exception or failure object from an error state.
no setter
- hasData → bool
-
Whether this state contains a valid, extractable data payload.
no setter
- hasError → bool
-
Whether this state is in a corrupted state containing an error or exception.
no setter
- hashCode → int
-
The hash code for this object.
no setterinherited
- isEmpty → bool
-
Whether this state is in an uninitialized or dormant state.
no setter
- phase → BloomPhase
-
The operational phase (BloomPhase.valid, BloomPhase.empty, or BloomPhase.error) of this state.
no setter
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
-
snapshot
→ BloomSnapshot<
T> -
The underlying snapshot representing this entity's current state.
no setter
- stackTrace → StackTrace
-
Extracts the call stack trace at the point of failure from an error state.
no setter
Methods
-
dataOrElse(
T fallback()) → T -
Safely extracts the underlying data, returning the result of
fallbackif not in a hasData state. -
equals(
covariant BloomState< T> other, [bool equals(T previous, T next)?]) → bool -
Determines if this state is equivalent to
otherusing an optional customequalscomparator. -
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited