Working with Streams

  • Working with Streams

Working with Streams

The dart:async library contains two types that are important for many Dart APIs: Stream and Future. We have talked about Future, Now let’s talk about Stream.

Asynchronous programming with streams in Dart allows you to work with a sequence of asynchronous events. Streams are particularly useful for handling events like user inputs, data from network requests, and real-time updates.

Key concepts of Streams

  • Single Subscription Streams: These streams can be listened to only once.
  • Broadcast Streams: These streams can have multiple listeners and are useful for events that are meant to be broadcast to multiple listeners.

How can you create and use Streams?

For Asynchronous programming which uses callback functions. A Stream is a way to get a sequence of values, such as events. Future, Stream, and more are in the dart:async library. The dart:async library works in both web apps and command-line apps.

Creating a Stream

It can be created through three ways:

  • Transforming existing streams
  • Creating a stream from scratch by using an ‘async’ function
  • Creating a stream by using a “StreamController”
  • Transforming an existing streams

It is used when you already have a stream.The most general approach is to create a new stream that waits for events on the original stream and then outputs new events.

Example

Stream<String> lines(Stream<String> source) async* {
  // Stores any partial line from the previous chunk.
  var partial = '';
  // Wait until a new chunk is available, then process it.
  await for (final chunk in source) {
    var lines = chunk.split('\n');
    lines[0] = partial + lines[0]; // Prepend partial line.
    partial = lines.removeLast(); // Remove new partial line.
    for (final line in lines) {
      yield line; // Add lines to output stream.
    }
  }
  // Add final partial line to output stream, if any.
  if (partial.isNotEmpty) yield partial;
}

Some of the transforming methods

.where((int x) => x.isEven) // Retain only even integer events.
.expand((var x) => [x, x]) // Duplicate each event.
.take(5) // Stop after the first five events.
  • Creating stream form scratch

Example

import 'dart:async';
void main() {
  // Create a stream that emits values 1 to 4 with a delay
  Stream<int> stream = Stream<int>.periodic(Duration(seconds: 1), (count) => count + 1).take(4);

  // Listen to the stream
  stream.listen((data) {
    print('Received: $data');
  }, onDone: () {
    print('Stream is done');
  });
}

Using a StreamController

If the events of your stream comes from different parts of your program, and not just from a stream or futures that can traversed by an async function, then use a StreamController to create and populate the stream. A StreamController gives you a new stream and a way to add events to the stream at any point, and from anywhere. Example

import 'dart:async';

void main() {
  // Create a broadcast stream
  StreamController<String> controller = StreamController<String>.broadcast();

  // Add listeners to the stream
  controller.stream.listen((data) {
    print('Listener 1: $data');
  });
  controller.stream.listen((data) {
    print('Listener 2: $data');
  });

  // Add data to the stream
  controller.sink.add('Hello');
  controller.sink.add('World');

  // Close the stream
  controller.close();
}