data_state 0.2.2 copy "data_state: ^0.2.2" to clipboard
data_state: ^0.2.2 copied to clipboard

outdated

A practical alternative to the AsyncSnapshot API

data_state #

tests pub.dev license

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

1
likes
0
pub points
6%
popularity

Publisher

unverified uploader

A practical alternative to the AsyncSnapshot API

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

async, freezed_annotation, rxdart, state_notifier

More

Packages that depend on data_state