Skip to content

futureware-tech/stream_with_value

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stream_with_value

pub package flutter build code coverage

About

If you ever found yourself:

  • listening to a single-subscription Stream and needing to access the latest value on demand (and not just in the listen callback);
  • confused and tired of tedious handling of lazy-loaded values in StreamBuilder, especially handling the edge cases when the value is not loaded (progress indicator);

then this package is for you. It provides an interface, which is an encapsulation of Stream and a value of type T, along with a set of convenient implementations and extensions. The most common to use is a StreamWithLatestValue, which automatically tracks the latest value emitted by the stream:

/// Abstract interface, the base of this package.
abstract class StreamWithValue<T> {
  /// The stream.
  Stream<T> get updates;
  /// The value.
  T get value;
  /// Whether the value is initialized.
  bool get loaded;
}

/// A specific implementation which simply tracks the latest value emitted by
/// the sourceStream.
class StreamWithLatestValue<T> implements StreamWithValue<T> {
  StreamWithLatestValue(Stream<T> sourceStream) { /* ... */ }

  factory StreamWithLatestValue.withInitialValue(
    Stream<T> sourceStream, {
    required T initialValue,
  }) { /* ... */ }
}

Note well: since subscribing to a single-subscription stream can be an expensive operation (e.g. it can initiate a network connection and start a download), StreamWithLatestValue would not subscribe to the stream unless you do (e.g. through updates.listen() or passing it to StreamBuilder). This means that the value will not be loaded until a subscription is active and the first value is emitted by the stream.

If you want a different behavior, consider other implementations of StreamWithValue offered by this package, for example, PushStreamWithValue.

There's more to love about this package, see the API reference for other helpers.

For Flutter users

A very common pattern in reactive programming in Flutter is to show a progress indicator while an element of UI is loading (e.g. from remote database). Flutter offers convenient StreamBuilder, which allows you to customize behavior when a stream is not loaded, but you have to implement it from scratch and insert branching. And what if the value for the stream has been loaded before, and the UI has just been rebuilt when user rotated the screen? This may cause flickering, and these two are exactly the problems StreamWithValue is here to solve. Use the convenience widgets: StreamBuilderWithValue and if you don't want to handle null or not loaded data, then its friend, DataStreamWithValueBuilder will save your day.

How to use

See the Install section on how to start using StreamWithValue in your project. You can use convenience wrappers to start improving your project right away:

StreamWithValue<T> _counter;

void initState() {
  _counter = StreamWithValue<int>(
    Stream<int>.periodic(const Duration(seconds: 1))
  );
}

// Before StreamWithValue, in the build() method:
child: StreamBuilder<int>(
  stream: _counter.updates,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    return snapshot.hasData
      ? Text(
          '${snapshot.data}',
          style: Theme.of(context).textTheme.headline4,
        )
      : CircularProgressIndicator();
  },
)

// If you turn the screen, you will see a progress indicator for a moment. This
// can be easily solved with StreamWithValue and StreamWithValueBuilder:
child: StreamBuilderWithValue<int>(
  streamWithValue: _counter,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    return snapshot.hasData
      ? Text(
          '${snapshot.data}',
          style: Theme.of(context).textTheme.headline4,
        )
      : CircularProgressIndicator();
  },
)

/// Or use convenience widget in the code, which will automatically render a
/// progress indicator if the value is not yet loaded:
child: DataStreamWithValueBuilder<int>(
  streamWithValue: _counter,
  builder: (BuildContext context, int value) => Text(
      '${snapshot.data}',
      style: Theme.of(context).textTheme.headline4,
  ),
)

See more in the Example section of the documentation. Happy streaming! Or building. Or both.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages