Effect class sealed
A declarative lifecycle-bound side-effect node that executes an untracked action upon upstream mutations.
Under the two-plane reactive architecture of the Bloom framework:
- Data Plane (Bloom): Evaluates and propagates state mutations downstream via the 2-Phase Push-Dirty / Lazy-Pull DAG.
- Lifecycle Plane (Tether): Declarative lifecycle entities governed by enclosing tracking scopes via BloomVia.tether or executed imperatively via EffectRunner.
- An Effect encapsulates an internal terminal (
_Terminal) that acts as a non-propagating terminal sink (_isNewerThanalways returnsfalse), executing pure side-effects (analytics, external I/O sync, storage, audio/haptic feedback) without mutating intermediate DAG nodes or propagating dirty pulses downstream.
Sub-Factory Pathways
Effect(...) ──> Manifest N-ary effect ([Effect])
├─ .unary(...) ──> Fast-path unary manifest effect
├─ .via(...) ──> Dynamic discovery effect
├─ .scoped(...) ──> Scoped effect with lifecycle context ([EffectScope])
│ ├─ .unary(...) ──> Scoped unary effect
│ └─ .via(...) ──> Scoped dynamic discovery effect
├─ .debounce(...) ──> Time-stabilized debounce effect
│ └─ .unary(...) ──> Unary debounce effect
├─ .throttle(...) ──> Rate-limited throttle effect
│ └─ .unary(...) ──> Unary throttle effect
└─ .group(...) ──> Composite effect group
System Architectural Flow
Upstream Bloom Invalidation
│
▼ (Push-Dirty Notification)
┌─────────────────────────┐
│ Schedule Terminal │ (_Scheduler.instance._scheduleTerminal)
└────────────┬────────────┘
│ (Scheduled Turn Execution)
▼
┌─────────────────────────┐
│ _resolveDirty()? │ (2-Phase Verification)
└────────────┬────────────┘
│
┌──────────────┴──────────────┐
(false)│ │(true)
▼ ▼
[Abort execution] ┌────────────────┐
(No actual mutation) │ Check guard()? │
└────────┬───────┘
│
┌──────────────┴──────────────┐
(false)│ │(true / no guard)
▼ ▼
[Skip execution] [Consume 1 take Ticket]
[Retain tickets] │
▼
[Execute action() Untracked]
│
┌────────────┴────────────┐
│ Exhausted take tickets? │
└────────────┬────────────┘
│(true)
▼
[Auto-Deactivate]
[Early Teardown]
Key Invariants & Architectural Benefits
- Zero-Glitch Terminal Sinks: Effects fire only after state transactions settle via the Bloom microtask scheduler, eliminating transient tearing and intermediate glitch states.
- Precondition Gating with Ticket Preservation: When
guardreturnsfalse, execution is aborted without consumingtaketickets. - Sound Early Teardown: An effect configured with
takeautomatically deactivates and detaches its subscription edges once the ticket quota reaches zero. - Unified Creation Pathways: Instances are constructed exclusively via the
Effect factories and sub-factories (
Effect(...),Effect.unary(...),Effect.via(...),Effect.scoped(...),Effect.debounce(...),Effect.throttle(...),Effect.group(...)). - Declarative Lifecycle Management: Tethering via
via.tether()automatically aligns effect lifecycles with the enclosing computation scope.
Warning (Anti-Pattern Prevention):
- Do NOT mutate observed
targetssynchronously insideactionwithout an exit condition, as it induces reentrant invalidation loops. - Do NOT instantiate new Effect objects inside build methods without caching or lifecycle tethering.
BAD:
// [Anti-Pattern] Synchronously mutating target inside its own effect without an exit condition
final counter = blooming(0);
Effect([counter.bloom], () {
counter.set(untracked(counter.bloom) + 1); // Triggers infinite invalidation loop!
});
GOOD:
// [Recommended] One-way terminal sink for external interactions
final counter = blooming(0);
Effect([counter.bloom], () {
analyticsService.logCounter(untracked(counter.bloom)); // Pure side-effect sink
});
Examples
final user = blooming('Alice');
final auth = blooming(true);
final syncEffect = Effect(
[user.bloom, auth.bloom],
() => syncSession(untracked(user.bloom), untracked(auth.bloom)),
take: 5,
);
- Inheritance
- Implemented types
- Available extensions
Constructors
-
Effect(Iterable<
Bloom> targets, void action(), {bool guard()?, int? take, void onUncaughtError(Object error, StackTrace stackTrace)?}) -
Creates a manifest Effect attached strictly to explicit
targets.factory -
Effect.group(Iterable<
Effect> effects) -
Bundles a collection of
effectsinto a composite Effect managing their aggregated lifecycle.factory - Effect.unary(Bloom target, void action(), {bool guard()?, int? take, void onUncaughtError(Object error, StackTrace stackTrace)?})
-
Creates an optimized unary manifest Effect attached strictly to a single explicit
target.factory - Effect.via(void action(BloomVia via), {bool guard(BloomVia via)?, void onUncaughtError(Object error, StackTrace stackTrace)?})
-
Creates an explicit dynamic discovery Effect using BloomVia.
factory
Properties
- debugCreationLocation → String?
-
Available on T, provided by the TetherDebugExtension extension
The exact code location where this tether was instantiated in debug mode.no setter -
debugDependencies
→ List<
BloomDebugSnapshot> -
Available on Effect, provided by the EffectDebugExtension extension
Debug-only list of active upstream dependencies currently monitored by this effect's terminal.no setter - debugLabel ↔ String?
-
Available on T, provided by the TetherDebugExtension extension
A developer-defined label to identify this tether in debug mode.getter/setter pair - hashCode → int
-
The hash code for this object.
no setterinherited
- isActive → bool
-
Whether the node is currently active (connected to a downstream terminal).
no setterinherited
- runner → EffectRunner
-
Available on Effect, provided by the EffectStartExtension extension
Creates a dormant, unstarted EffectRunner for this effect.no setter - runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
Methods
-
debugTrace(
[String? label, int traceCount = 5]) → void -
Available on T, provided by the TetherDebugExtension extension
Captures the current stack trace and assigns an optional label to this tether. -
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
start(
) → EffectRunner -
Available on Effect, provided by the EffectStartExtension extension
Instantiates an EffectRunner and immediately starts observation and execution. -
toString(
) → String -
A string representation of this object.
inherited
-
withDebugLabel(
String label) → T -
Available on T, provided by the TetherDebugExtension extension
Assigns a developer-defined debug label and returns this tether.
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited
Constants
- debounce → const EffectDebounceFunctor
- Sub-factory providing time-domain debounce rate-limiting.
- scoped → const EffectScopedFunctor
- Sub-factory providing managed lifecycle EffectScope callbacks with cleanup registration.
- throttle → const EffectThrottleFunctor
- Sub-factory providing time-domain throttle rate-limiting.