Streams in Dart are a powerful way to handle asynchronous data. They allow you to work with sequences of data that are delivered asynchronously, such as user inputs, file I/O, or network requests. In this section, we will cover the basics of streams, how to create and use them, and some common patterns and practices.
Key Concepts
- Stream: A sequence of asynchronous events.
- StreamController: A controller that allows you to create and manage a stream.
- StreamSubscription: An object that represents a subscription to a stream.
- Single-subscription Stream: A stream that can only be listened to once.
- Broadcast Stream: A stream that can be listened to multiple times.
Creating Streams
Using StreamController
A StreamController
is used to create a stream and add events to it.
import 'dart:async'; void main() { // Create a StreamController final controller = StreamController<int>(); // Get the stream from the controller final stream = controller.stream; // Listen to the stream stream.listen((data) { print('Received: $data'); }); // Add data to the stream controller.add(1); controller.add(2); controller.add(3); // Close the stream controller.close(); }
Explanation
- StreamController: Manages the stream and allows adding events.
- stream.listen: Subscribes to the stream and handles incoming data.
- controller.add: Adds data to the stream.
- controller.close: Closes the stream when done.
Types of Streams
Single-subscription Stream
A single-subscription stream can only be listened to once.
void main() { final controller = StreamController<int>(); final stream = controller.stream; stream.listen((data) { print('Received: $data'); }); controller.add(1); controller.add(2); controller.add(3); controller.close(); }
Broadcast Stream
A broadcast stream can be listened to multiple times.
void main() { final controller = StreamController<int>.broadcast(); final stream = controller.stream; stream.listen((data) { print('Listener 1: $data'); }); stream.listen((data) { print('Listener 2: $data'); }); controller.add(1); controller.add(2); controller.add(3); controller.close(); }
Practical Example: Timer Stream
Let's create a stream that emits a value every second.
import 'dart:async'; void main() { final controller = StreamController<int>(); int counter = 0; Timer.periodic(Duration(seconds: 1), (timer) { counter++; controller.add(counter); if (counter == 5) { controller.close(); timer.cancel(); } }); controller.stream.listen((data) { print('Timer: $data'); }); }
Explanation
- Timer.periodic: Creates a timer that runs a callback every second.
- controller.add: Adds the current counter value to the stream.
- controller.close: Closes the stream after 5 seconds.
Common Patterns
Transforming Streams
You can transform streams using methods like map
, where
, and expand
.
void main() { final controller = StreamController<int>(); final stream = controller.stream; final transformedStream = stream.map((data) => 'Number: $data'); transformedStream.listen((data) { print(data); }); controller.add(1); controller.add(2); controller.add(3); controller.close(); }
Combining Streams
You can combine multiple streams using methods like merge
and zip
.
import 'dart:async'; void main() { final controller1 = StreamController<int>(); final controller2 = StreamController<int>(); final stream1 = controller1.stream; final stream2 = controller2.stream; final combinedStream = StreamZip([stream1, stream2]); combinedStream.listen((data) { print('Combined: $data'); }); controller1.add(1); controller2.add(2); controller1.add(3); controller2.add(4); controller1.close(); controller2.close(); }
Exercises
Exercise 1: Create a Stream
Create a stream that emits the numbers 1 to 10 and prints each number.
import 'dart:async'; void main() { final controller = StreamController<int>(); final stream = controller.stream; stream.listen((data) { print('Number: $data'); }); for (int i = 1; i <= 10; i++) { controller.add(i); } controller.close(); }
Exercise 2: Transform a Stream
Create a stream that emits the numbers 1 to 5, transforms each number to its square, and prints the result.
import 'dart:async'; void main() { final controller = StreamController<int>(); final stream = controller.stream; final transformedStream = stream.map((data) => data * data); transformedStream.listen((data) { print('Square: $data'); }); for (int i = 1; i <= 5; i++) { controller.add(i); } controller.close(); }
Summary
In this section, we covered the basics of streams in Dart, including how to create and use them, the difference between single-subscription and broadcast streams, and some common patterns like transforming and combining streams. Streams are a powerful tool for handling asynchronous data, and understanding them is crucial for effective Dart programming.
Dart Programming Course
Module 1: Introduction to Dart
- Introduction to Dart
- Setting Up the Development Environment
- Your First Dart Program
- Basic Syntax and Structure