data_state 0.2.3
data_state: ^0.2.3 copied to clipboard
A practical alternative to the AsyncSnapshot API
data_state #
Easily produce and consume loading/error/data states in your application.
DataState
is a StateNotifier
-based alternative to AsyncSnapshot
.
- Produce events: notifier API
- Consume events: notifier API & stream API
It also supports a reload
function to restart a data loading cycle.
This is the anatomy of an immutable DataState
object:
final state = DataState({
T model,
bool isLoading = false,
Object exception,
StackTrace stackTrace,
Future<void> Function() reload,
});
👩🏾💻 Usage #
Consuming state #
Flutter example:
@override
Widget build(BuildContext context) {
return StateNotifierBuilder<DataState<List<Post>>>(
stateNotifier: repository.watchAll(),
builder: (context, state, _) {
return Column(
children: [
if (state.isLoading)
CircularProgressIndicator(),
if (state.hasException)
ExceptionWidget(state.exception),
if (state.hasModel)
ModelWidget(model),
],
);
}
);
}
The reload
function can be combined with a gesture detector or reloader widget:
Example 1:
GestureDetector(
onTap: () => state.reload(), // will trigger a rebuild with isLoading = true
child: _child,
)
Example 2:
body: EasyRefresh.builder(
controller: _refreshController,
onRefresh: () async {
await state.reload();
_refreshController.finishRefresh();
},
Want to consume events via streams?
DataStateNotifier
actually exposes an RxDart ValueStream
:
@override
Widget build(BuildContext context) {
final stream = repo.watchPosts().stream;
return StreamBuilder<List<Post>>(
initial: stream.value,
stream: stream,
builder: (context, snapshot) {
// snapshot as usual
}
);
}
🎸 Producing state #
Example:
DataStateNotifier<List<T>> watchAll() {
final _reload = () async {
notifier.state = notifier.state.copyWith(isLoading: true);
try {
notifier.state = notifier.state.copyWith(model: await loadAll());
} catch (e) {
notifier.state = notifier.state.copyWith(exception: DataException(e));
}
};
final notifier = DataStateNotifier<List<T>>(DataState(model: [], reload: _reload));
_load();
hiveBox.watch().forEach((model) {
notifier.state = notifier.state.copyWith(model: model, isLoading: false);
}).catchError((Object e) {
notifier.state = notifier.state.copyWith(exception: DataException(e));
});
return notifier;
}
⁉ FAQ #
Why is DataState
not a freezed union? #
This would allow us to do the following destructuring:
state.when(
data: (data) => Text(data),
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
This turns out to be impractical in Flutter widgets, as there are cases where we need to render loading/error messages in addition to data, and not instead of data.
Does DataStateNotifier depend on Flutter? #
No. It can used with pure Dart.
➕ Collaborating #
Please use Github to ask questions, open issues and send PRs. Thanks!
📝 License #
MIT