stated_result 0.2.0
stated_result: ^0.2.0 copied to clipboard
A bloc that accepts async source as input
stated_result #
A library built upon Bloc_Flutter and Provider to make it easy to support async operation
This library includes 4 sub-modules:
Multi-State Result #
The library includes 4 different types of MultiStateResult, each has a different state to indicates its internal state. The type is an immutable data type, and can be pattern matched like enum.
ActionResult: A result has 2 states:CompletedandFailed. It can be used to represents the result of action without return value.AsyncActionResult: A result has 4 states:Pending,Waiting,Completed, andFailed. It represents the full lifecycle of an async action without a return value.QueryResult<T>: A result has 2 states:SucceededandFailed, in whichSucceededwould hold a value of given typeT. It represents the result of a query that returns a certain type of data.AsyncQueryResult<T>: A result has 4, states:Pending,Waiting,Suceeded, andFailed. It represents a full lifecycle of a query that returns a certain type of data. Potentially, an extra stateDefaultcan be used. It is likePending- indicates the query hasn't been started, but also holds avaluelikeSucceeded. It can be used to indicate the scenario where the app provides default value as optimistic updates.
How it works #
Typically, the result can be used to represent the result of an async operation, such as calling API or loading data from DB, or time consuming data processing.
Typically, the result itself should be AsyncActionResult or AsyncQueryResult, depends on whether the app cares about the return value or not, and UI would render accordingly, such as loading screen error screen or the successful screen.
The query/action itself would be represented as Future<QueryResult>/Future<ActionResult>, which can be used to update AsyncQueryResult and AsyncActionResult accordingly.
Typically, AsyncActionResult would be held by a Bloc or ValueNotifier, which will be discussed below.
Convert Sync Result to Async Result #
asAsyncResult can convert sync result into its async counterpart accordingly.
AsyncActionResult asyncActionResult = actionResult.asAsyncResult();
AsyncQueryResult asyncQueryResult = queryResult.asAsyncResult();
Convert Future Types #
Future.asActionResult can materialise any future into Future<ActionResult>. The new future would always fulfill. If the original future fails, it captures the error into a FailedResult accordingly.
Future<ActionResult>.asActionResult would flatten the hierarchy and respect the failed value from the original future.
Future<T>.asQueryResult would materialise the future of T into Future<QueryResult<T>>. The new future would always fulfill. If original future fails it captures the error into a FailedResult accordingly.
Multi-State Result Builder #
MultiStateResult type provides map and/or mapOr method to pattern match and map the value, which can be used to build UI manually.
ActionResultBuilder and QueryResultBuilder #
In practice, ActionResultBuilder and QueryResultBuilder provided by this library can be more convenient, with few extra features.
ActionResultBuilder and QueryResultBuilder accept different builder function to build the UI according to the state of the AsyncActionResult or the AsyncQueryResult.
A simple example:
Widget build(BuildContext context, AsyncActionResult result) =>
ActionResultBuilder(
result: result,
pendingBuilder: (_context) => Center(child: Text("No Data")),
waitingBuilder: (_context) => Center(child: CircularProgressIndicator()),
failedBuilder: (_context, error, _stackTrace) => Center(
child: Text("Error: $error"),
),
completedBuilder: (_context) => Center(child: Text("Completed")),
);
Non critical state default builders #
For ActionResultBuilder and QueryResultBuilder, builder is considered as a critical builder, which is mandatory each time the widget is instantiated, while pendingBuilder, waitingBuilder, and failedBuilder are optional and can be omitted.
When the given builder is omitted, the ResultBuilder will search the widget hierarchy to find any default builder that has been given. Just like Text would search DefaultTextStyle if textStyle is not explicitly given.
Accordingly, DefaultPendingResultBuilder, DefaultWaitingResultBuilder, and DefaultFailedResultBuilder can be used to provide those default builders to their children along the widget tree.
DefaultResultBuilder #
If more than one default builder needs to be configured, rather than nested DefaultXXXResultBuilder, which results in a relatively ugly code, DefaultResultBuilder can be used, which allow configuring multiple default builders at once.
DefaultResultBuilder can be used to set up the unified UI style of a portion of the screen.
Global default builders #
If optional builders are omitted and no default builders are given, the ResultBuilder widget would use something called global default builder to render the UI.
Which can be set up via
DefaultPendingResultBuilder.globalBuilderDefaultWaitingResultBuilder.globalBuilderDefaultFailedResultBuilder.globalBuilder
It also can be configured in batch via DefaultResultBuilder.setGlobalBuilder
Bloc Integration #
Library provided the classes to integrate the [MultiStateResult] to Bloc
This library uses Cubit instead of Bloc, which is a simplified and easier-to-use Bloc.
ActionCubit: ACubitholdsAsyncActionResult, it also provides protected methods to update its value from different kinds ofFutureQueryCubit<T>: ACubitholdsAsyncQueryResult<T>, it also provides protected methods to update its value from different kinds ofFuture
Both ActionCubit and QueryCubit can be provided to the widget tree via BlocProvider
Bloc Builder and Bloc Consumer #
Like BlocBuilder and BlocConsumer provided by Bloc_Flutter library, the following types are provided:
ActionBlocBuilder:BlocBuilderthat consumesActionCubitviaActionResultBuilderActionBlocConsumer:BlocConsumerthat consumesActionCubitviaActionResultBuilderQueryBlocBuilder:BlocBuilderthat consumesQueryCubitviaQueryResultBuilderQueryBlocConsumer:BlocConsumerthat consumesQueryCubitviaQueryResultBuilder
ValueNotifier Integration #
Besides Cubit, this library also provides the integration to ValueNotifier and ListenableBuilder.
ActionNotifier: AValueNotifierholdsAsyncActionResult, it also provides protected methods to update its value from different kinds ofFutureQueryNotifier<T>: AValueNotifierholdsAsyncQueryResult<T>, it also provides protected methods to update its value from different kinds ofFuture
Listenable Builder #
Like ListenableBuilder, the following classes are provided:
ActionListenableBuilder:ListenableBuilderthat consumesActionNotifierviaActionResultBuilderQueryListenableBuilder:ListenableBuilderthat consumesQueryNotifierviaQueryResultBuilder
Deal with an empty list or nullable value #
It could be kind of annoying to build a ListView/GridView from a List, which could be empty, or to build UI from a field of a JSON that could be null.
EmptyableContentBuilder is the widget designed to deal with those cases.
Widget build(BuildContext context) {
return QueryBlocBuilder(
bloc: context.read<MyStringListBloc>(),
builder: (_, list) => EmptyableContentBuilder(
value: list,
emptyBuilder: (_) => Center(child: Text("List is empty")),
builder: (_, list) => ListView.builder(
itemCount: list.length,
itemBuilder: (_, index) => ItemWidget(item: list[index]),
),
);
)
}
EmptyableContentBuilder supports empty checking for List Map Iterable and String by default, it treats null as empty too.
To make BlankableBuilder work with other types, blankChecker checker can be provided.