Listening to a stream captures the sequence of results coming from an event-like action, such as clicking on a button in a web page or opening a file with the openRead
method. These results are data that can be processed, but the errors that occur are also part of the stream. Dart can work with streams in a very functional way, such as filtering the results with where
or mapping the results to a new stream (for a complete list of these methods, refer to https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.Stream). To modify the incoming results, we can also use a transformer; this recipe shows you how to do this (refer to the project transforming_stream
).
In our script, we have a list, persons
, where the items are themselves lists consisting of a name and a gender. We want to walk through the list and emit a greeting message based on the gender of the person, but if the gender is unknown, we skip that person. The following code shows us how we can do this with a transformer:
import 'dart:async'; var persons = [ ['Carter', 'F'], ['Gates', 'M'], ['Nuryev', 'M'], ['Liszt', 'U'], ['Besançon', 'F'] ]; void main() {
We need to perform the following steps to transform the streams:
var stream = ne w Stream.fromIterable(persons);
var transformer = new StreamTransformer.fromHandlers(handleData: convert);
stream .where((value) => value[1] != 'U') .transform(transformer) .listen((value) => print("$value")); } convert(value, sink) { // create new value from the original value var greeting = "Hello Mr. or Mrs. ${value[0]}"; if (value[1] == 'F') { greeting = "Hello Mrs. ${value[0]}"; } else if (value[1] == 'M') { greeting = "Hello Mr. ${value[0]}"; } sink.add(greeting); }
After performing the preceding steps, we get the following output:
Hello Mrs. Carter
Hello Mr. Gates
Hello Mr. Nuryev
Hello Mrs. Besançon
To turn a list into a stream, we used the fromIterable
method as in step 1. Discarding some values from the stream can be done with where
; see the first clause in step 3.
Step 2 details how to transform a stream. This method takes an object (here called transformer
) of the class StreamTransformer
, which allows you to change the contents of the stream. The constructor named fromHandlers
takes an optional handleData
argument that calls our callback function convert
for each value passed from the stream. The convert
option builds a new value based on the content of the old value and adds it in place of the old value of the sink
variable. Only those transformed values are output on the stream, passed on to listen
, and processed there. The sink
option is an instance of the abstract class StreamSink
, which is a generic destination of data and can be implemented by any data receiver.
We have already used transform
in this chapter when reading a file with openRead
, as shown in the following code:
Stream<List<int>> input = file.openRead(); input .transform(UTF8.decoder) .transform(const LineSplitter())
The inputStream
stream is a List<int>
list, and thus strongly typed. First, the incoming integers are transformed into a stream of UTF-8 characters, and then the input is split into subsequent lines. Instead of transform, we could have used the map
method on the stream as well.
HttpRequest in the browser does not support getting the response as a stream. To work along that pattern, you have to use WebSockets
(refer to Chapter 7, Working with Web Servers).