BloomWidget class abstract

A reactive widget that dynamically and explicitly tracks all Bloom dependencies declared via BloomVia within build.

Seamlessly connects Flutter widget rebuild cycles with Bloom's 2-Phase LPDAG engine using 1-pass zero-allocation epoch diffing. Rather than managing manual streams, listeners, or ChangeNotifier subscriptions, dependencies are registered dynamically and explicitly via the injected BloomVia parameter in build.

System Architectural Flow

[ Upstream Bloom<T> Mutation ]
       │
       │ (Phase 1: 0-Compute Push-Dirty Invalidation)
       ▼
[ RawComputedTerminal<Widget>.onInvalidated: markNeedsBuild() ]
       │
       │ (Flutter Engine Frame Pipeline Scheduling)
       ▼
[ BloomElement.rebuild() -> performRebuild() ]
       │
       │ (Phase 2: Lazy-Pull Evaluation via terminal.data)
       ▼
┌────────────────────────────────────────────────────────────┐
│ If Clean: Returns cached Widget in O(1) (0-Compute)        │
│ If Dirty: Evaluates BloomWidget.build(context, via)         │
└────────────────────────────────────────────────────────────┘

Key Invariants & Architectural Benefits

  1. 0-Compute Reactive Memoization: When upstream state triggers frame invalidation but dependencies resolve to clean (e.g. unchanged distinct intermediates), terminal.data returns the cached widget in $O(1)$ without re-evaluating build. (Note: Ancestor widget rebuilds pass new widget instances through Element.update, which re-evaluates build to honor updated widget parameters; use const or composed.child for ancestor isolation).
  2. Zero Heap Overhead in Hot Path: Dynamic edge tracking utilizes 1-pass zero-allocation epoch stamping, avoiding intermediate list allocations during rebuilds.
  3. Dormancy Garbage Collection: When the widget unmounts, all active graph subscriptions detach synchronously via RawTerminal.deactivate.
  4. No Stateful Mutators: BloomWidget is intentionally immutable and declarative. Never mutate state synchronously inside build.

Warning (Anti-Pattern Prevention):

  • Do NOT read untracked values directly without wrapping in via(...), as untracked reads will not trigger widget rebuilds when state changes.
  • Do NOT mutate reactive state inside build, as this triggers reentrancy loops and violates Flutter's rendering contract.

BAD:

// [Anti-Pattern] Reading untracked directly bypasses dependency registration
class BadCounterWidget extends BloomWidget {
  BadCounterWidget(this.countBloom, {super.key});
  final Bloom<int> countBloom;

  @override
  Widget build(BuildContext context, BloomVia via) {
    return Text('Count: ${untracked(countBloom)}'); // NEVER rebuilds!
  }
}

GOOD:

// [Recommended] Explicitly observe dependencies via BloomVia
class GoodCounterWidget extends BloomWidget {
  GoodCounterWidget(this.countBloom, {super.key});
  final Bloom<int> countBloom;

  @override
  Widget build(BuildContext context, BloomVia via) {
    return Text('Count: ${via(countBloom)}'); // Dynamically tracked
  }
}

Examples

// Pure reactive state node:
final counter = blooming(0);

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return CounterView(counterBloom: counter.bloom);
  }
}

class CounterView extends BloomWidget {
  const CounterView({required this.counterBloom, super.key});
  final Bloom<int> counterBloom;

  @override
  Widget build(BuildContext context, BloomVia via) {
    return Text('Count: ${via(counterBloom)}');
  }
}
Inheritance
Implementers

Constructors

BloomWidget({Key? key})
Initializes a BloomWidget with an optional key.
const

Properties

hashCode → int
The hash code for this object.
no setterinherited
key → Key?
Controls how one widget replaces another widget in the tree.
finalinherited
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

build(BuildContext context, BloomVia via) → Widget
Describes the part of the user interface represented by this widget, with explicit BloomVia tracking.
createElement() → BloomElement
Inflates this configuration to a concrete instance.
override
debugDescribeChildren() → List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) → String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String
Returns a one-line detailed description of the object.
inherited
toStringShort() → String
A short, textual description of this widget.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited