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
