BloomAsyncBuilder<T> class final

A declarative reactive widget providing structured 4-state asynchronous branching (data, loading, error, empty) with native Stale-While-Revalidate (SWR) preservation.

Seamlessly maps Bloom's 3-State reactive model along with collection emptiness onto dedicated UI builders, preventing nested if-else ladders and eliminating UI flicker during background refresh.

System Architectural Flow

┌────────────────────────────────────────────────────────────────────────┐
│                       BloomAsyncBuilder Flow                            │
├────────────────────────────────────────────────────────────────────────┤
│                                                                        │
│                           [ Upstream Bloom<T> ]                         │
│                                     │                                  │
│                   ┌─────────────────┴─────────────────┐                │
│                   ▼                                   ▼                │
│             [ hasData: true ]                   [ hasData: false ]     │
│                   │                                   │                │
│            ┌──────┴──────┐                     ┌──────┴──────┐         │
│            ▼             ▼                     ▼             ▼         │
│        [ Empty? ]  [ Valid Data ]       [ hasError? ]   [ isEmpty? ]   │
│            │             │                     │             │         │
│            ▼             ▼                     ▼             ▼         │
│         empty()       data()                error()       loading()    │
│                                                                        │
└────────────────────────────────────────────────────────────────────────┘

Key Invariants & Architectural Benefits

  1. Flicker-Free SWR (Stale-While-Revalidate): When preserveDataOnPending is true (the default) and the upstream node retains prior valid data (e.g. via source.retained), background re-evaluations do not trigger the loading placeholder. The previous valid data view remains fully rendered and active.
  2. Automatic Collection Emptiness Detection: If empty is provided and the valid payload is an empty Iterable or Map, routes automatically to empty.
  3. Deterministic 4-State UI Branching: Eliminates nested conditionals and ternary cascades across asynchronous boundaries.
  4. Pending-Aware Empty vs. Loading: When in BloomPhase.empty, renders loading while pending is true. When pending is false, renders empty if provided (falling back to loading if empty is omitted).

Warning (Anti-Pattern Prevention):

  • Avoid creating async state pipelines inside build methods without caching; always connect bloom to a persistent state node (e.g. computed.future or stream.toBloom).

BAD:

// [Anti-Pattern] Pipeline recreated per build pass
Widget build(BuildContext context) {
  return BloomAsyncBuilder(
    bloom: myFuture.toBloom(), // Re-evaluates every frame!
    pending: isPendingBloom,
    data: (context, data) => Text('$data'),
    loading: (context) => const CircularProgressIndicator(),
  );
}

GOOD:

// [Recommended] Connect to cached controller or derived pipeline
BloomAsyncBuilder<UserProfile>(
  bloom: profileBloom,
  pending: profilePendingBloom,
  data: (context, user) => ProfileCard(user: user),
  loading: (context) => const ShimmerPlaceholder(),
);

Examples

BloomAsyncBuilder<UserProfile>.tuple(
  fetchUserProfile().toBloomTuple(),
  data: (context, user) => ProfileCard(user: user),
  loading: (context) => const ShimmerPlaceholder(),
  error: (context, error, stack) => ErrorRetryButton(error: error),
  empty: (context) => const EmptyProfileNotice(),
);

2. Explicit Primary Constructor (when bloom and pending are managed independently)

BloomAsyncBuilder<UserProfile>(
  bloom: userBloom,
  pending: isPendingBloom,
  data: (context, user) => ProfileCard(user: user),
  loading: (context) => const ShimmerPlaceholder(),
  error: (context, error, stack) => ErrorRetryButton(error: error),
  empty: (context) => const EmptyProfileNotice(),
);
Inheritance

Constructors

BloomAsyncBuilder({Key? key, required Bloom<T> bloom, required Bloom<bool> pending, required Widget data(BuildContext context, T data), required Widget loading(BuildContext context), Widget error(BuildContext context, Object error, StackTrace stackTrace)?, Widget empty(BuildContext context)?, bool preserveDataOnPending = true})
Creates a structured 4-state asynchronous branching widget.
const
BloomAsyncBuilder.tuple(({Bloom<T> data, Bloom<bool> pending}) tuple, {Key? key, required Widget data(BuildContext context, T data), required Widget loading(BuildContext context), Widget error(BuildContext context, Object error, StackTrace stackTrace)?, Widget empty(BuildContext context)?, bool preserveDataOnPending = true})
Creates a structured 4-state asynchronous branching widget directly from a named record tuple ({Bloom<T> data, Bloom<bool> pending}).

Properties

bloom → Bloom<T>
The upstream reactive Bloom node being observed.
final
data → Widget Function(BuildContext context, T data)
Builder callback invoked when valid payload data is available.
final
empty → Widget Function(BuildContext context)?
Optional builder callback invoked when data is empty or node is explicitly empty.
final
error → Widget Function(BuildContext context, Object error, StackTrace stackTrace)?
Optional builder callback invoked when an error occurs during evaluation.
final
hashCode → int
The hash code for this object.
no setterinherited
key → Key?
Controls how one widget replaces another widget in the tree.
finalinherited
loading → Widget Function(BuildContext context)
Builder callback invoked when the node is in its initial uninitialized or loading state.
final
pending → Bloom<bool>
Required pending indicator pipeline Bloom<bool>.
final
preserveDataOnPending → bool
Whether to retain and render prior valid data during background re-evaluation.
final
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.
override
createElement() → BloomElement
Inflates this configuration to a concrete instance.
inherited
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