Stream Dasar
Aliran data berkelanjutan: konsep Stream dan listen.
Dari satu nilai ke banyak nilai
Future memberikan SATU nilai di masa depan. Tapi banyak data datang sebagai aliran: pesan chat yang terus berdatangan, lokasi GPS yang update tiap detik, progres download 1%...2%... Stream adalah Future yang berulang: urutan nilai async yang tiba seiring waktu.
Analogi: Future itu seperti memesan satu paket (datang sekali). Stream itu seperti langganan koran (datang tiap hari). Keduanya async, tapi Stream tidak pernah "selesai" dalam sekali kirim.
Kenapa Stream ada?
Karena dunia nyata penuh data kontinu: sensor, websocket chat, event klik pengguna, perubahan database realtime. Tanpa Stream, kamu harus polling manual ("cek tiap detik ada yang baru?") yang boros dan lambat.
Membuat dan mendengar Stream
// Stream.periodic: memancarkan nilai tiap interval
void main() async {
var stream = Stream.periodic(
Duration(seconds: 1),
(n) => 'Detik ke-$n',
).take(5); // ambil 5 saja, lalu berhenti
// Cara 1: listen (callback per nilai)
await for (var s in stream) {
print(s);
}
print('Stream selesai.');
}Dua cara mengonsumsi:
await for: loop yang menunggu tiap nilai tiba, berhenti saat stream selesai. Butuh fungsiasync..listen(): mendaftarkan callback, tidak memblokir.
Contoh kedua dengan .listen():
void main() {
var stream = Stream.fromIterable([10, 20, 30]);
stream.listen(
(n) => print('Dapat: $n'),
onDone: () => print('Selesai!'),
onError: (e) => print('Error: $e'),
);
print('listen() tidak menunggu, langsung lanjut');
}Output: "listen() tidak menunggu...", lalu "Dapat: 10", "Dapat: 20", "Dapat: 30", "Selesai!". listen mendaftarkan handler lalu langsung lanjut; nilai diproses saat tiba.
StreamController: membuat stream sendiri
import 'dart:async';
void main() async {
var controller = StreamController<String>();
// Produsen: kirim nilai kapan saja
controller.sink.add('Halo');
controller.sink.add('Dunia');
await controller.close(); // tutup: tidak ada nilai lagi
// Konsumen
await for (var s in controller.stream) {
print(s);
}
}StreamController memisahkan produsen (sink.add) dan konsumen (stream). Pola ini fondasi BLoC di Flutter: UI mengirim event ke sink, dan mendengar state dari stream.
Single-subscription vs broadcast
void main() async {
var single = Stream.fromIterable([1, 2, 3]);
// single.listen(...); single.listen(...); // ERROR: hanya 1 pendengar!
var broadcast = Stream.fromIterable([1, 2, 3]).asBroadcastStream();
broadcast.listen((n) => print('A: $n'));
broadcast.listen((n) => print('B: $n')); // OK
}- Single-subscription (default): satu pendengar saja. Cocok untuk data yang tidak boleh terlewat (baca file).
- Broadcast: banyak pendengar, tapi pendengar yang telat tidak mendapat nilai lama. Cocok untuk event UI.
Kesalahan umum
1. await for di fungsi tidak async
// SALAH
// void main() {
// await for (var x in stream) {} // ERROR
// }// BENAR
Future<void> main() async {
await for (var x in Stream.fromIterable([1, 2])) {
print(x);
}
}2. Lupa close StreamController
Controller yang tidak di-close membocorkan resource dan membuat program tidak pernah selesai (event loop menunggu). Selalu await controller.close() atau pakai try-finally.
3. Dua listen pada single-subscription stream
void main() {
var s = Stream.fromIterable([1]);
s.listen((n) => print('satu: $n'));
// s.listen((n) => print('dua: $n')); // StateError!
}Butuh banyak pendengar? Ubah ke broadcast dengan .asBroadcastStream().
Kesimpulan
Stream = aliran nilai async seiring waktu. Konsumsi dengan await for (menunggu) atau .listen() (callback). Buat sendiri dengan StreamController. Pahami beda single vs broadcast sebelum berlangganan.
Catatan teknis: Stream di Dart bisa pause, resume, dan cancel lewat objek StreamSubscription yang dikembalikan
.listen(). Ini penting untuk menghemat resource, mis. berhenti mendengar GPS saat layar ditutup.
Tantangan
Stream suhu sensor
Buat stream suhu() dengan async* yang yield suhu 25, 26, 27 (jeda 500ms tiap yield). Konsumsi dengan await for dan cetak tiap nilai.
// TODO: Stream<int> suhu() async*
Future<void> main() async {
// TODO: await for
}