StreamBuilder — widget Flutter yang secara otomatis membangun ulang antarmuka saat menerima data baru dari aliran asinkron. Berbeda dengan FutureBuilder yang bekerja dengan hasil satu kali, StreamBuilder mendukung pembaruan UI secara terus-menerus sepanjang siklus hidup Stream. Menurut dokumentasi resmi Flutter (2026), StreamBuilder digunakan dalam aplikasi waktu nyata: obrolan, umpan berita, pemantauan sensor, dan ticker keuangan. Ini adalah alat kunci pemrograman reaktif, di mana UI mencerminkan status data tanpa panggilan setState manual.
Poin Utama
StreamBuilder — adalah widget dari paket Flutter SDK yang berlangganan ke Stream dan membangun ulang elemen anaknya pada setiap peristiwa baru aliran. StreamBuilder menerima objek Stream dan mengembalikan widget berdasarkan snapshot terakhir yang diperoleh dari aliran.
Dalam arsitektur Flutter, StreamBuilder termasuk dalam grup widget Builder yang memisahkan pembangunan UI dari status data. Berbeda dengan StatefulWidget, di mana perubahan status memerlukan panggilan eksplisit setState, StreamBuilder bereaksi terhadap peristiwa asinkron secara otomatis, menyederhanakan kode dan mengurangi risiko kesalahan sinkronisasi.
Berbeda dengan FutureBuilder, yang memproses satu nilai asinkron, StreamBuilder dirancang untuk aliran data berkelanjutan. FutureBuilder berakhir setelah menerima hasil pertama, sementara StreamBuilder terus mendengarkan aliran dan memperbarui UI pada setiap peristiwa baru.
StreamBuilder digunakan dalam semua skenario di mana data masuk secara terus-menerus: koneksi WebSocket, panggilan balik sensor, notifikasi Firebase, antrian peristiwa Bluetooth, dan transmisi status aplikasi melalui BLoC. Menurut analisis proyek Flutter di GitHub (2025), StreamBuilder berada di tiga besar widget Builder yang paling banyak digunakan bersama FutureBuilder dan LayoutBuilder.
Kesimpulan: gunakan StreamBuilder di mana pun UI harus mencerminkan data yang terus berubah, menghindari manajemen status manual melalui StatefulWidget.
StreamBuilder berlangganan ke Stream pada saat pembangunan dan berhenti berlangganan saat widget dihancurkan. Setiap kali Stream mengeluarkan peristiwa, StreamBuilder menerima AsyncSnapshot baru dan memanggil fungsi builder untuk membangun ulang UI.
Proses terdiri dari tiga tahap. Pertama: StreamBuilder membuat langganan ke Stream yang diberikan melalui metode stream.listen. Kedua: pada setiap peristiwa, StreamBuilder memperbarui AsyncSnapshot internal dan menandai widget sebagai “kotor” untuk dibangun ulang. Ketiga: framework memanggil fungsi builder dengan snapshot baru, dan UI menampilkan data terkini.
Penting: StreamBuilder menggunakan StreamSubscription secara internal. Jika Stream diberikan secara langsung, StreamBuilder berlangganan sekali saat inisialisasi. Jika Stream berubah (misalnya, saat pembangunan ulang induk), StreamBuilder berhenti berlangganan dari aliran lama dan berlangganan ke yang baru. Perilaku ini dikendalikan oleh parameter initialData dan buildWhen, yang memungkinkan optimalisasi jumlah pembangunan ulang.
Kesimpulan: memahami siklus hidup langganan adalah dasar penggunaan StreamBuilder yang benar. Manajemen aliran yang salah menyebabkan kebocoran memori atau data usang di UI.
Properti connectionState dari objek AsyncSnapshot menentukan pada tahap mana StreamBuilder berada dalam bekerja dengan aliran. Ada empat status yang dibedakan: none, waiting, active, done.
None — status awal ketika Stream belum mulai mengirimkan data. Dalam status ini snapshot.connectionState sama dengan ConnectionState.none, dan snapshot.data adalah null. Biasanya dalam status ini ditampilkan placeholder atau menunggu peristiwa pertama. Jika Stream tidak menyediakan data awal, StreamBuilder mulai dari status ini.
Waiting — status menunggu data dari aliran asinkron. Stream aktif, tetapi data belum tiba. Status ini muncul, misalnya, saat memuat data dari jaringan atau membuka koneksi berdurasi panjang. Dalam status ini biasanya ditampilkan CircularProgressIndicator atau kerangka pemuatan.
Active — aliran mengeluarkan data dan UI menampilkan informasi terkini. Dalam status ini snapshot.hasData true, dan snapshot.data berisi nilai terakhir dari aliran. Jika Stream adalah Broadcast Stream, status aktif dapat hidup berdampingan dengan menunggu data baru.
Done — aliran selesai, tidak akan ada data baru. Snapshot.data berisi nilai terakhir yang dikirimkan sebelum aliran ditutup. Jika aliran selesai dengan sukses, snapshot.hasError false. Status ini digunakan untuk menampilkan hasil akhir: pesan “Pemuatan selesai” atau transisi ke layar berikutnya.
Kesimpulan: saat membangun UI melalui StreamBuilder, keempat status harus diproses agar antarmuka menampilkan pemuatan, data, kesalahan, dan penyelesaian dengan benar.
StreamController — adalah kelas dari paket dart:async yang membuat dan mengelola Stream. StreamController memungkinkan menambahkan data, memproses kesalahan, dan menutup aliran, mengendalikan siklus hidupnya.
StreamController terdiri dari dua jenis: single-subscription (satu pelanggan) dan broadcast (banyak pelanggan). Pengontrol single-subscription hanya menerima satu pendengar dalam satu waktu — langganan ulang akan menyebabkan pengecualian. Pengontrol broadcast memungkinkan beberapa StreamBuilder mendengarkan satu aliran secara bersamaan, yang berguna untuk BLoC dan status bersama aplikasi.
Saat membuat StreamController melalui StreamController<T>.broadcast(), data yang ditambahkan sebelum langganan pertama tidak diputar ulang ke pelanggan baru. Jika perlu mendapatkan nilai terakhir saat terhubung, gunakan BehaviourSubject dari paket rxdart, yang menyimpan cache peristiwa terakhir.
Setelah selesai bekerja dengan pengontrol, controller.close() harus dipanggil. Tidak memanggil close menyebabkan kebocoran sumber daya: aliran tetap terbuka, pelanggan tetap berada di memori, dan GC tidak membebaskan objek terkait.
Kesimpulan: gunakan StreamController dengan manajemen siklus hidup yang eksplisit. Untuk aliran single-subscription — pengontrol standar, untuk status bersama — pengontrol broadcast atau BehaviourSubject.
Contoh 1 mendemonstrasikan penghitung waktu mundur menggunakan StreamController dan StreamBuilder.
import 'dart:async';
class TimerWidget extends StatefulWidget {
const TimerWidget({super.key});
final StreamController<int> controller = StreamController<int>();
void startTimer() {
int count = 0;
Timer.periodic(Duration(seconds: 1), (timer) {
controller.sink.add(count++);
if (count > 10) {
controller.close();
timer.cancel();
}
});
}
}
Dalam contoh, pengontrol dibuat untuk menghasilkan angka dari 0 hingga 10 dengan interval 1 detik. Setelah mencapai 10, close dipanggil dan aliran berakhir. StreamBuilder, yang berlangganan ke stream pengontrol ini, akan menampilkan setiap nilai baru.
Contoh 2 — penggunaan StreamBuilder dengan Broadcast Stream untuk menampilkan data dari beberapa sumber.
final StreamController<String> broadcastController =
StreamController<String>.broadcast();
StreamBuilder<String>(
stream: broadcastController.stream,
initialData: 'Waiting for data...',
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Data: ${snapshot.data}');
},
)
Contoh kedua menunjukkan pemrosesan semua status: initialData untuk tampilan awal, waiting untuk indikator pemuatan, hasError untuk kesalahan, dan data untuk hasil yang berhasil. Pola ini adalah standar untuk kode produksi dengan StreamBuilder.
Kesimpulan: gunakan initialData untuk menghindari layar kosong pada saat pertama dan selalu proses hasError untuk menampilkan kesalahan dengan benar kepada pengguna.
Kesalahan 1: membuat Stream baru setiap kali pembangunan ulang induk. Jika Stream diberikan melalui ekspresi yang membuat objek baru pada setiap pembangunan, StreamBuilder berhenti berlangganan dari aliran lama dan berlangganan ke yang baru, menyebabkan siklus pembangunan ulang tak terbatas. Solusi: gunakan variabel remembered atau StatefulWidget dengan Stream tetap.
Kesalahan 2: tidak adanya penanganan kesalahan. Stream dapat mengeluarkan kesalahan melalui controller.sink.addError, dan jika builder tidak memeriksa snapshot.hasError, pengguna melihat layar kosong atau pemuatan tak terbatas. Solusi: selalu periksa hasError dan tampilkan pesan yang dapat dimengerti.
Kesalahan 3: kebocoran memori karena StreamController tidak ditutup. Jika pengontrol tidak ditutup di dispose, aliran terus ada dan GC tidak membebaskan memori. Solusi: panggil controller.close() di dispose dan dengarkan peristiwa done untuk tindakan akhir.
Kesalahan 4: menggunakan StreamBuilder dengan fungsi builder yang lambat. Karena builder dipanggil pada setiap peristiwa aliran, perhitungan berat di dalamnya menyebabkan penurunan bingkai. Solusi: pindahkan perhitungan ke isolat terpisah atau gunakan Stream.map untuk transformasi data.
Kesimpulan: StreamBuilder adalah alat yang kuat tetapi menuntut. Awasi siklus hidup Stream, proses kesalahan, dan hindari operasi berat di builder.
Pertanyaan yang Sering Diajukan
FutureBuilder dirancang untuk hasil asinkron satu kali: berlangganan ke Future, menerima satu nilai, dan menyelesaikan pekerjaannya. StreamBuilder berlangganan ke Stream, yang dapat mengeluarkan banyak nilai dari waktu ke waktu, dan membangun ulang UI pada setiap peristiwa baru.
AsyncSnapshot — objek yang tidak dapat diubah yang berisi status langganan saat ini (connectionState), nilai terakhir yang diterima (data), dan objek kesalahan (error), jika aliran mengeluarkan pengecualian.
Kesalahan ditangani melalui properti snapshot.hasError dan snapshot.error di fungsi builder. Jika aliran mengeluarkan kesalahan melalui metode sink.addError, AsyncSnapshot menerima error, dan builder harus menampilkan pesan yang sesuai atau UI cadangan.
Ya, jika Stream adalah broadcast (dibuat melalui StreamController.broadcast). Stream single-subscription hanya mengizinkan satu pelanggan. Untuk berbagi satu aliran di antara beberapa widget, gunakan pengontrol broadcast atau paket rxdart dengan BehaviourSubject.
Gunakan parameter buildWhen untuk memfilter peristiwa di mana UI perlu dibangun ulang. Juga terapkan Stream.transformer atau Stream.where untuk memfilter data sebelum dikirim ke StreamBuilder.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga