Dart: Creating Streams From Iterables

An iterable is a collection of items you can go through one by one, like a list or a set. It holds data in order, and you can loop over it easily.

In Dart, you can turn an iterable into a stream — a sequence of events that send data over time. Creating streams from iterables lets you work with data in a way that fits with asynchronous programming.

Using streams from iterables makes your code flexible. You can listen to each item as it arrives, add delays, or combine streams easily.

Using Stream.fromIterable()

You can convert a list (an iterable) into a stream using Stream.fromIterable(). This sends each item in the list one by one as a stream event.

Here’s an example that streams dog breeds:

Stream<String> dogBreeds() => Stream.fromIterable(['Beagle', 'Bulldog', 'Dalmatian']);

void main() {

  dogBreeds().listen((breed) {
    print('Breed: $breed');
  });

}

Each dog breed is sent as a separate event in the stream.

Listening to a Stream from Iterable

To get the items from a stream created from an iterable, you listen to the stream. Each item is received one by one.

Here’s an example that prints each dog breed from the stream (no different from above example):

Stream<String> dogBreeds() => Stream.fromIterable(['Beagle', 'Bulldog', 'Dalmatian']);

void main() {

  dogBreeds().listen((breed) => print('Breed: $breed'));

}

Listening lets you handle each item as it arrives from the stream.

Creating Stream from a Set

Streams can be created from any iterable, including sets. Sets are collections of unique items without a specific order.

Here’s an example that streams chess pieces from a set:

Stream<String> chessPieces() => Stream.fromIterable({'King', 'Queen', 'Pawn'});

void main() {

  chessPieces().listen((piece) {
    print('Chess piece: $piece');
  });

}

Each chess piece is sent one by one from the set as a stream event.

Using Iterable with Custom Objects

You can create streams from iterables of custom objects. First, make a class, then create a list of objects, and stream them.

Here’s an example streaming animals with names and ages:

class Animal {

  final String name;
  final int age;
  
  Animal(this.name, this.age);
  
}

Stream<Animal> animalStream() => Stream.fromIterable([
  Animal('Leo', 3),
  Animal('Mia', 5),
]);

void main() {

  animalStream().listen((animal) {
    print('Animal: ${animal.name}, Age: ${animal.age}');
  });

}

Each animal object is sent one by one as a stream event.

Adding Delay Between Items (fun twist)

You can add a delay between items when streaming from an iterable using an async* function with yield and await. This lets each item arrive one after another with a pause.

Here’s an example that streams dog barks with a 1-second delay between each:

Stream<String> delayedBarks(List<String> barks) async* {

  for (var bark in barks) {
    await Future.delayed(Duration(seconds: 1));
    yield bark;
  }

}

void main() {

  delayedBarks(['Woof!', 'Bark!', 'Arf!']).listen((bark) {
    print(bark);
  });

}

This makes the stream send items slowly, like a real dog barking one after another.

Combining Streams From Multiple Iterables

You can join streams from different iterables by creating an async* function that yields from each stream in order.

Here’s an example combining two lists of fruit names into one stream:

Stream<String> combinedFruits() async* {

  yield* Stream.fromIterable(['Apple', 'Banana']);
  yield* Stream.fromIterable(['Cherry']);

}

void main() {

  combinedFruits().listen((fruit) {
    print('Fruit: $fruit');
  });

}

This way, the stream sends all items from the first list, then continues with items from the second list.

Conclusion

Creating streams from iterables like lists, sets, or custom objects is simple with Stream.fromIterable(). You can send items one by one as stream events.

To add delays between items or combine multiple streams, use async* functions with yield and yield*. These tools help you handle data clearly and smoothly in your Dart programs.