BloomSignal class abstract base

A specialized reactive Bloom node representing a discrete event signal.

Implements Bloom<int> by emitting a monotonically increasing dirty version sequence (tick count). Observing this node inside reactive scopes (e.g. computed, effect, or reactive widget scopes) causes downstream subscribers to re-evaluate whenever the signal is emitted via BloomSignalController.emit.

System Architectural Flow

[User Tap / External Event]
             │
             ▼ (signalController.emit())
┌───────────────────────────┐
│   BloomSignalController    │ (Producer Handle)
└───────────────────────────┘
             │
             ▼ (.bloom)
┌───────────────────────────┐
│        BloomSignal         │ (Consumer View, implements Bloom<int>)
└───────────────────────────┘
             │
             ▼ (Explicit Dependency Manifest via BloomVia)
┌────────────────────────────────────────┐ ┌────────────────────────────────┐
│ computed.future((via) {                │ │ computed((via) {               │
│   via(refreshTrigger.bloom);            │ │   final tick = via(sig.bloom)...│
│   return api.fetchUserFeed();          │ │   return 'Event tick #$tick';  │
│ });                                    │ │ });                            │
└────────────────────────────────────────┘ └────────────────────────────────┘

Two Idiomatic Consumption Patterns

Pattern A: Discrete Invalidation / Refetch Trigger (Zero-Data Pulse)

When a computation needs to be invalidated or re-evaluated whenever an event occurs, declare the dependency via via(refreshTrigger.bloom):

final refreshTrigger = blooming.signal();

// Declaratively re-evaluates/refetches whenever `refreshTrigger.emit()` is called:
final userFeed = computed.future((via) {
  via(refreshTrigger.bloom); // Subscribes to discrete event pulse
  return api.fetchUserFeed();
});

Pattern B: Explicit Dynamic Read via BloomVia (When Tick Count is Needed)

When the computation specifically reads or displays the tick sequence number:

final pulseSignal = blooming.signal();

final pulseCounter = computed((via) {
  final tick = via(pulseSignal.bloom);
  return 'Event tick #$tick';
});

Key Capabilities

  1. Zero-Allocation Version Sequence: Emits internal sequence ticks (0 -> 1 -> 2 ...), guaranteeing that downstream .distinct() filters will never drop or suppress consecutive pulses.
  2. Producer-Consumer Decoupling: Emitters hold the BloomSignalController handle, while consumers only receive read-only BloomSignal.
  3. 100% LPDAG Integration: Drop-in compatible with BloomVia, Effect, and all pipeline transformations.

Warning (Anti-Pattern Prevention):

  • Do NOT expose BloomSignalController directly to public UI layers; expose the read-only BloomSignalController.bloom view to prevent arbitrary signal emissions.
  • Do NOT read signals via untracked(...) directly without wrapping in via(...), as untracked reads will miss discrete pulses.

BAD:

// [Anti-Pattern] Exposing mutable signal controller handle directly to UI consumers
class RefreshService {
  final refreshSignal = blooming.signal(); // Callers can invoke emit() uncontrollably!
}

GOOD:

// [Recommended] Encapsulate signal controller and expose read-only BloomSignal
class RefreshService {
  final _refreshSignal = blooming.signal();
  BloomSignal get refreshSignal => _refreshSignal.bloom;

  void refresh() => _refreshSignal.emit();
}

Examples

final refreshSignal = blooming.signal();

final userFeed = computed.future((via) {
  via(refreshSignal.bloom); // Dynamically tracks signal pulse
  return fetchFeed();
});

// Trigger refresh
refreshSignal.emit();
Implemented types

Constructors

BloomSignal()

Properties

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
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

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