A pragmatic StreamBuilder with sensible defaults

Overview

DataStreamBuilder

Pub

A pragmatic StreamBuilder with sensible defaults.

😩 The problem with StreamBuilder

StreamBuilder is an essential tool to work with BLoCs or generally any stream.

But with enough streams in our codebase, a lot of boilerplate is introduced. Code gets repetitive and can't be easily reused:

StreamBuilder(
  stream: bloc.stock,
  builder: (context, AsyncSnapshot<Stock> snapshot) {
    if (snapshot.hasData) {
      return Text(snapshot.data.stock.toString());
    } else if (snapshot.hasError) {
      return Text(snapshot.error.toString());
    }
    return Center(child: CircularProgressIndicator());
  },
),

DataStreamBuilder aims to fix these issues for the common usecase:

DataStreamBuilder(
  stream: bloc.stock,
  builder: (context, Stock stock) => Text(stock.local.toString())
),

This library provides default builders for the loading and error states (can be supplied as loadingBuilder and errorBuilder), and only calls builder when data is present.

Depending on your current StreamBuilders this could be a drop-in replacement. In any case, it's super easy to migrate to DataStreamBuilder.

Note: if you need fine-grained control over ConnectionStates to the underlying computation, just use StreamBuilder.

👩🏾‍💻 Usage

Using defaults for loading and error states:

DataStreamBuilder<List<Post>>(
  stream: Repository.of<Post>().findAll(),
  builder: (context, List<Post> posts) => ListView(
    children: posts.map((post) => ListTile(title: Text(post.body))).toList(),
  )
)

In case you are wondering, these are the defaults:

Default loading widget:

(context) => Center(child: CircularProgressIndicator());

Default error widget:

(context, dynamic error) {
  error = error is Exception ? error.toString() : 'Error: $error';
  return Center(child: Text(
      error,
      textDirection: TextDirection.ltr,
      style: TextStyle(backgroundColor: Colors.red, color: Colors.white),
    )
  );
};

Another example with custom loading and error widgets:

DataStreamBuilder<List<Post>>(
  stream: Repository.of<Post>().findAll(),
  loadingBuilder: (context) => Center(child: Text('Loading posts...')),
  errorBuilder: (context, error) => PostErrorView(error),
  builder: (context, List<Post> posts) => ListView(
    children: posts.map((post) => ListTile(title: Text(post.body))).toList(),
  )
)

Initial values can also be supplied:

// a stream that is a RxDart ValueObservable
final stream = Repository.of<Post>().findAll();

DataStreamBuilder<List<Post>>(
  stream: stream, // async access to stream
  initialData: stream.value, // sync access to latest value
  builder: (context, List<Post> posts) => ListView(
    children: posts.map((post) => ListTile(title: Text(post.body))).toList(),
  )
)

See tests and the Example tab for a full example.

🛠 Extending

We can easily extend the class in order to provide our own app-wide defaults.

For instance, we could implement a branded loader:

class BrandedDataStreamBuilder<T> extends DataStreamBuilder<T> {

  static final brandedLoadingBuilder = (context) => Text('Custom branded loading...');

  BrandedDataStreamBuilder({
    Key key,
    @required Stream<T> stream,
    @required DataWidgetBuilder<T> builder,
    DataErrorWidgetBuilder errorBuilder
  }) :
    assert(builder != null),
    super(
      key: key,
      stream: stream,
      builder: builder,
      loadingBuilder: brandedLoadingBuilder,
      errorBuilder: errorBuilder
    );
}

In action:

sample

Collaborating

Please use Github to open issues and send PRs. Thanks 🙌

📝 License

MIT

You might also like...
An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable.

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable. For example the RX stream is a BehaviorSubject or a ReplaySubject.

Jon Samwell 8 Jan 22, 2022
A pure Dart utility library that checks for an internet connection by opening a socket to a list of specified addresses, each with individual port and timeout. Defaults are provided for convenience.

data_connection_checker A pure Dart utility library that checks for an internet connection by opening a socket to a list of specified addresses, each

Kristiyan Mitev 103 Nov 29, 2022
A StreamBuilder alternative that provides builder and event callbacks

mainstream A StreamBuilder alternative that provides builder and event callbacks. See the Futuristic package for a similar API for working with Future

Martin Rybak 6 Apr 9, 2021
An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable.

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable. For example the RX stream is a BehaviorSubject or a ReplaySubject.

Jon Samwell 8 Jan 22, 2022