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
- Flicker-Free SWR (Stale-While-Revalidate): When preserveDataOnPending is
true(the default) and the upstream node retains prior valid data (e.g. viasource.retained), background re-evaluations do not trigger the loading placeholder. The previous valid data view remains fully rendered and active. - Automatic Collection Emptiness Detection: If empty is provided and the valid payload is an empty Iterable or Map, routes automatically to empty.
- Deterministic 4-State UI Branching: Eliminates nested conditionals and ternary cascades across asynchronous boundaries.
- Pending-Aware Empty vs. Loading: When in BloomPhase.empty, renders loading while
pending is
true. When pending isfalse, renders empty if provided (falling back to loading if empty is omitted).
Warning (Anti-Pattern Prevention):
- Avoid creating async state pipelines inside
buildmethods without caching; always connect bloom to a persistent state node (e.g.computed.futureorstream.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
1. Ergonomic Tuple Shorthand (Recommended with toBloomTuple() or retainedTuple())
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
-
- Object
- DiagnosticableTree
- Widget
- BloomWidget
- BloomAsyncBuilder
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