dartstreamasyncMahir5 mnt baca

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

dart
// 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 fungsi async.
  • .listen(): mendaftarkan callback, tidak memblokir.

Contoh kedua dengan .listen():

dart
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

dart
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

dart
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

dart
// SALAH
// void main() {
//   await for (var x in stream) {} // ERROR
// }
dart
// 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

dart
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.

text
// TODO: Stream<int> suhu() async*

Future<void> main() async {
  // TODO: await for
}