Stream

Aliran data dari waktu ke waktu: membuat Stream dengan async*/yield, mendengarkan dengan listen dan await for, transformasi (map, where, take), StreamController, dan broadcast.

Part 16 dari 20 ± 50 menit Gratis

Future satu nilai, Stream banyak nilai

Future menghasilkan satu nilai di masa depan. Stream menghasilkan banyak nilai sepanjang waktu. Bayangkan pipa air: air (data) mengalir terus, dan kamu memasang keran untuk menampungnya. Contoh: detik jam yang berjalan, pesan chat yang masuk, lokasi GPS yang berubah, progres unduhan.

Membuat Stream dengan async* dan yield

Stream<int> hitungMundur(int dari) async* {
  for (int i = dari; i > 0; i--) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;                       // kirim satu nilai ke aliran
  }
}

Function bertanda async* mengembalikan Stream; setiap yield mengirim satu elemen. (yield* meneruskan seluruh Stream lain.)

Mendengarkan: await for

Future<void> main() async {
  await for (final angka in hitungMundur(3)) {
    print(angka);                   // 3, 2, 1
  }
  print('Selesai');
}

await for mengulang setiap kali ada data baru, dan selesai saat Stream berakhir. Ini cara paling mudah dibaca.

Mendengarkan: listen

var langganan = hitungMundur(3).listen(
  (data) => print('Data: $data'),
  onError: (e) => print('Error: $e'),
  onDone: () => print('Aliran selesai'),
);

// langganan.pause(); langganan.resume(); langganan.cancel();

listen tidak memblokir; ia mendaftarkan callback. Objek StreamSubscription yang dikembalikan bisa dijeda atau dibatalkan. Selalu batalkan langganan bila sudah tidak dipakai agar tidak ada kebocoran memori.

Mengolah Stream

Mirip koleksi, Stream punya operator transformasi yang menghasilkan Stream baru:

var angka = Stream.fromIterable([1, 2, 3, 4, 5, 6]);

await for (var n in angka.where((n) => n.isEven).map((n) => n * 10)) {
  print(n);                  // 20, 40, 60
}

await angka.take(3).toList();       // [1, 2, 3]
await angka.length;                 // 6
await angka.first;                  // 1
var jumlah = await angka.fold(0, (a, b) => a + b);   // 21
OperatorFungsi
mapUbah tiap elemen
whereSaring elemen
take(n) / skip(n)Ambil / lewati n elemen pertama
distinct()Buang elemen berurutan yang sama
toList(), first, last, lengthMengumpulkan hasil (mengembalikan Future)

StreamController: membuat sumber sendiri

import 'dart:async';

var pengendali = StreamController<String>();

pengendali.stream.listen((pesan) => print('Diterima: $pesan'));

pengendali.add('Halo');
pengendali.add('Dart');
pengendali.addError('Ada masalah');   // kirim error ke aliran
await pengendali.close();             // tandai aliran selesai

Controller adalah "keran + pipa" sekaligus: kamu add data dari satu sisi, dan pendengar menerima dari sisi lain. Ini dipakai untuk membangun fitur seperti chat, notifikasi, dan state management.

Single-subscription vs broadcast

BiasaBroadcast
Jumlah pendengarSatu sajaBanyak
MembuatStreamController()StreamController.broadcast()
Data sebelum ada pendengarDitahanHilang

Memasang dua listen pada Stream biasa menghasilkan error Bad state: Stream has already been listened to. Ubah ke .asBroadcastStream() bila perlu banyak pendengar.

Periodik

var tiap = Stream.periodic(Duration(seconds: 1), (n) => n);   // 0, 1, 2, ...
await for (var detik in tiap.take(3)) {
  print('Detik ke-$detik');
}

Future atau Stream?

SituasiPilih
Satu hasil (respons API, baca berkas)Future
Rangkaian peristiwa / data berkelanjutanStream

Error umum

  • Program tidak berhenti → StreamController tidak di-close(), atau langganan tidak dibatalkan.
  • Bad state: Cannot add event after closing → add setelah close.
  • Memakai await for di function yang tidak async.

Praktikkan langsung, gratis

Tulis dan jalankan kode langsung di browser, dapatkan penilaian otomatis. Daftar gratis kapan saja untuk menyimpan progres dan meraih sertifikat.

Coba latihan sekarang, tanpa daftar
Bagikan: WhatsApp Facebook LinkedIn X