ValueStreams
ValueStreams are a powerful concept in the Flood toolkit, built upon the foundation provided by the rxdart (opens in a new tab) package. While rxdart
offers a wide range of utilities for handling streams, it lacks some essential features that Flood addresses. One of the key limitations of rxdart
is that transforming a ValueStream
always results in an asynchronous Stream
instead of another ValueStream
. Flood extends the functionality of ValueStream
and BehaviorSubject
from rxdart
to provide a seamless way to transform ValueStreams
into other ValueStreams
, allowing you to retrieve the value of the stream synchronously.
FutureValues
Throughout the Flood codebase, you will frequently encounter ValueStream<FutureValue<T>>
, where FutureValue
represents the state of a Future, such as loading
, loaded
, or error
. This concept is inspired by the freezed (opens in a new tab) package. A ValueStream<FutureValue<T>>
essentially means a synchronous stream that tracks the status of a Future
and how its value changes over time.
Naming Convention
In Flood, if a function or getter ends with an X, it indicates that it returns a ValueStream
. For example, AuthService.static.memory().accountX
returns a ValueStream
of the currently logged-in Account. You can fetch this value synchronously by calling authService.accountX.value
to know the current logged-in account, and you can also listen to or map the stream like you would with a regular Stream to make your app reactive.
Transforming ValueStreams
Flood provides a set of utility functions to transform ValueStreams while preserving their synchronous value access. Here are some of the notable utilities:
-
mapWithValue
: This method is similar to the standard map (opens in a new tab) operator, but it allows you to retrieve the current value of the transformed stream synchronously.final mappedStream = valueStream.mapWithValue((value) => value * 2); final currentValue = mappedStream.value;
-
switchMapWithValue
: This method is equivalent to the switchMap (opens in a new tab) operator, but it maintains the ability to retrieve the current value of the resulting stream synchronously.final switchedStream = valueStream.switchMapWithValue((value) => anotherValueStream); final currentValue = switchedStream.value;
-
asyncMapWithValue
: This method is similar to the asyncMap (opens in a new tab) operator, but it allows you to retrieve the latest value emitted by the stream. However, it's important to note that the async mapper only works when the stream is being listened to.final asyncMappedStream = valueStream.asyncMapWithValue((value) async => await someAsyncOperation(value)); asyncMappedStream.listen((value) { final latestValue = asyncMappedStream.value; });
Combining ValueStreams
Flood also provides utilities to combine multiple ValueStreams
into a single ValueStream
while preserving the ability to access the combined value synchronously.
-
combineLatestWithValue
: This method is similar to the combineLatest (opens in a new tab) operator, but it allows you to retrieve the current combined value synchronously.final combinedStream = [valueStream1, valueStream2].combineLatestWithValue((values) => values.reduce((a, b) => a + b)); final currentCombinedValue = combinedStream.value;
Awaiting ValueStreams
Flood introduces the waitUntil
method, which allows you to wait for a specific condition to be met on a ValueStream
. It returns a Future
that resolves when the provided predicate function returns true
.
final result = await valueStream.waitUntil((value) => value > 10);
Converting Futures to ValueStreams
Flood provides the asValueStream
extension method on Future
, which converts a Future
into a ValueStream
that emits FutureValue
s representing the current state of the Future
. This is particularly useful when working with asynchronous operations and wanting to handle their state reactively.
final futureValueStream = myFuture.asValueStream();
futureValueStream.listen((futureValue) {
if (futureValue is FutureLoading) {
// Handle loading state
} else if (futureValue is FutureLoaded) {
// Handle loaded state
} else if (futureValue is FutureError) {
// Handle error state
}
});