data_state 0.3.0
data_state: ^0.3.0 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
👩🏾💻 Usage #
Consuming state #
Flutter example:
(Note: This example depends on flutter_data_state which is a separate package)
@override
Widget build(BuildContext context) {
return DataStateBuilder<List<Post>>(
notifier: repo.watchPosts(),
builder: (context, state, notifier, _) {
return Column(
children: [
if (state.isLoading)
CircularProgressIndicator(),
if (state.hasException)
ExceptionWidget(state.exception),
if (state.hasModel)
ShowPost(state.model),
],
);
}
);
}
The notifier also supports a reload
function to restart a data loading cycle. It can be combined with a gesture detector or reloader widget:
Example 1:
GestureDetector(
onTap: () => notifier.reload(), // will trigger a rebuild with isLoading = true
child: _child,
)
Example 2:
body: EasyRefresh.builder(
controller: _refreshController,
onRefresh: () async {
await notifier.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
}
);
}
This is the anatomy of an immutable DataState
object:
final state = DataState({
T model,
bool isLoading = false,
Object exception,
StackTrace stackTrace,
});
Caching the notifier #
For caching and shielding from crazy amount of rebuilds, use the lazyNotifier
parameter:
@override
Widget build(BuildContext context) {
return DataStateBuilder<List<Post>>(
key: Key(key), // optional
lazyNotifier: () => repo.watchPosts(),
builder: (context, state, notifier, _) {
// ...
}
);
}
Supply different key
parameters to control the caching!
🎸 Producing state #
Example:
DataStateNotifier<List<Post>> watchPosts() {
final notifier = DataStateNotifier<List<Post>>(
DataState(model: getLocalPosts()),
reload: (notifier) async {
notifier.state = notifier.state.copyWith(isLoading: true);
notifier.state = DataState(model: await loadPosts());
},
onError: (notifier, error, stackTrace) {
notifier.state = notifier.state
.copyWith(exception: error, stackTrace: stackTrace);
},
);
// start cycle
return notifier..reload();
}
The DataStateNotifier
constructor takes:
state
as the first positional argumentreload
andonError
as optional named arguments
⁉ 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 data-driven Flutter apps as there are cases where we need to render loading/error messages in addition to data – 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 #
See LICENSE