StreamBuilder, asenkron bir akıştan yeni veri alındığında arayüzü otomatik olarak yeniden oluşturan bir Flutter widget'ıdır. Tek bir sonuçla çalışan FutureBuilder'ın aksine, StreamBuilder Stream'in tüm yaşam döngüsü boyunca sürekli UI güncellemelerini destekler. Resmi Flutter dokümantasyonuna (2026) göre StreamBuilder, gerçek zamanlı uygulamalarda kullanılır: sohbetler, haber akışları, sensör izleme ve finansal tikker'lar. UI'nin manuel setState çağrıları olmadan veri durumunu yansıttığı reaktif programlamanın anahtar bir aracıdır.
Önemli Noktalar
StreamBuilder, Flutter SDK paketinden bir widget'tır ve bir Stream'e abone olur, her yeni akış olayında alt öğesini yeniden oluşturur. StreamBuilder bir Stream nesnesi kabul eder ve akıştan alınan en son snapshot'a dayalı bir widget döndürür.
Flutter mimarisinde StreamBuilder, UI oluşturmayı veri durumundan ayıran Builder widget'ları grubuna aittir. Durum değişikliğinin açık bir setState çağrısı gerektirdiği StatefulWidget'ın aksine, StreamBuilder asenkron olaylara otomatik olarak tepki verir, kodu basitleştirir ve senkronizasyon hataları riskini azaltır.
Tek bir asenkron değeri işleyen FutureBuilder'ın aksine, StreamBuilder sürekli veri akışları için tasarlanmıştır. FutureBuilder ilk sonucu aldıktan sonra sonlanırken, StreamBuilder akışı dinlemeye ve her yeni olayda UI'yı güncellemeye devam eder.
StreamBuilder, verilerin sürekli olarak geldiği tüm senaryolarda kullanılır: WebSocket bağlantıları, sensör callback'leri, Firebase bildirimleri, Bluetooth olay kuyrukları ve BLoC aracılığıyla uygulama durumu yayını. GitHub'daki Flutter projelerinin analizine (2025) göre StreamBuilder, FutureBuilder ve LayoutBuilder ile birlikte en çok kullanılan üç Builder widget'ı arasındadır.
Sonuç: UI'nin sürekli değişen verileri yansıtması gereken her yerde, StatefulWidget aracılığıyla manuel durum yönetiminden kaçınarak StreamBuilder kullanın.
StreamBuilder, oluşturma anında bir Stream'e abone olur ve widget yok edildiğinde aboneliği iptal eder. Stream her olay yaydığında, StreamBuilder yeni bir AsyncSnapshot alır ve UI'yı yeniden oluşturmak için builder fonksiyonunu çağırır.
Süreç üç aşamadan oluşur. Birinci: StreamBuilder, stream.listen yöntemi aracılığıyla iletilen Stream'e bir abonelik oluşturur. İkinci: her olayda StreamBuilder, dahili AsyncSnapshot'ı günceller ve widget'ı yeniden oluşturma için kirli olarak işaretler. Üçüncü: framework, builder fonksiyonunu yeni snapshot ile çağırır ve UI mevcut verileri görüntüler.
Önemli: StreamBuilder dahili olarak StreamSubscription kullanır. Stream doğrudan iletilirse, StreamBuilder başlatma sırasında bir kez abone olur. Stream değişirse (örneğin, bir üst öğe yeniden oluşturulduğunda), StreamBuilder eski akıştan aboneliğini iptal eder ve yenisine abone olur. Bu davranış, yeniden oluşturma sayısını optimize etmeye izin veren initialData ve buildWhen parametreleri tarafından kontrol edilir.
Sonuç: abonelik yaşam döngüsünü anlamak, StreamBuilder'ı doğru kullanmanın temelidir. Yanlış akış yönetimi, bellek sızıntılarına veya UI'da güncel olmayan verilere yol açar.
AsyncSnapshot nesnesinin connectionState özelliği, StreamBuilder'ın akış işlemenin hangi aşamasında olduğunu belirler. Dört durum vardır: none, waiting, active, done.
None, Stream'in henüz veri iletmeye başlamadığı başlangıç durumudur. Bu durumda, snapshot.connectionState ConnectionState.none'a eşittir ve snapshot.data null'dır. Genellikle bu durumda bir yer tutucu veya bekleme göstergesi görüntülenir. Stream başlangıç verisi sağlamazsa, StreamBuilder bu durumdan başlar.
Waiting, asenkron bir akıştan veri bekleme durumudur. Stream aktiftir, ancak veriler henüz gelmemiştir. Bu durum, örneğin ağdan veri yüklenirken veya uzun süreli bir bağlantı açılırken ortaya çıkar. Bu durumda genellikle bir CircularProgressIndicator veya iskelet yükleyici gösterilir.
Active — akış veri yayar ve UI güncel bilgileri görüntüler. Bu durumda, snapshot.hasData true'dur ve snapshot.data akıştan en son değeri içerir. Akış bir Broadcast Stream ise, aktif durum yeni veri bekleme ile bir arada bulunabilir.
Done — akış tamamlanmıştır, yeni veri gelmeyecektir. Snapshot.data, akış kapatılmadan önce iletilen son değeri içerir. Akış başarıyla tamamlandıysa, snapshot.hasError false'dur. Bu durum, nihai sonucu görüntülemek için kullanılır: “Yükleme tamamlandı” gibi bir mesaj veya sonraki ekrana geçiş.
Sonuç: StreamBuilder aracılığıyla UI oluştururken, arayüzün yükleme, veri, hata ve tamamlanmayı doğru şekilde gösterebilmesi için dört durumun da işlenmesi gerekir.
StreamController, dart:async paketinden bir sınıftır ve bir Stream oluşturur ve yönetir. StreamController, veri ekleme, hataları işleme ve akışı kapatma ile yaşam döngüsünü kontrol etmeye izin verir.
StreamController iki tiptir: single-subscription (bir abone) ve broadcast (birden çok abone). Single-subscription denetleyicisi aynı anda yalnızca bir dinleyici kabul eder — ikinci bir abonelik bir istisna oluşturur. Broadcast denetleyicisi, birden çok StreamBuilder'ın aynı anda aynı akışı dinlemesine izin verir; bu, BLoC ve paylaşılan uygulama durumu için kullanışlıdır.
StreamController<T>.broadcast() aracılığıyla bir StreamController oluştururken, ilk abonelikten önce eklenen veriler yeni abonelere tekrar oynatılmaz. Bağlantı sırasında en son değeri almak için, son olayı önbelleğe alan rxdart paketindeki BehaviourSubject kullanılır.
Denetleyici ile çalışma tamamlandıktan sonra controller.close() çağrılmalıdır. Close çağrılmazsa kaynak sızıntıları oluşur: akış açık kalır, aboneler bellekte kalır ve GC ilişkili nesneleri serbest bırakmaz.
Sonuç: StreamController'ı açık yaşam döngüsü yönetimi ile kullanın. Single-subscription akışları için standart denetleyiciyi, paylaşılan durum için broadcast denetleyiciyi veya BehaviourSubject'i kullanın.
Örnek 1, StreamController ve StreamBuilder kullanarak bir geri sayım zamanlayıcısını gösterir.
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();
}
});
}
}
Örnekte, 1 saniye aralıklarla 0'dan 10'a kadar sayılar üreten bir denetleyici oluşturulur. 10'a ulaştıktan sonra close çağrılır ve akış sonlanır. Bu denetleyicinin akışına abone olan StreamBuilder, her yeni değeri görüntüleyecektir.
Örnek 2 — birden çok kaynaktan veri görüntülemek için Broadcast Stream ile StreamBuilder kullanımı.
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('Hata: ${snapshot.error}');
}
return Text('Veri: ${snapshot.data}');
},
)
İkinci örnek, tüm durumların işlenmesini gösterir: ilk görüntüleme için initialData, yükleme göstergesi için waiting, hatalar için hasError ve başarılı sonuç için data. Bu desen, StreamBuilder ile üretim kodu için standarttır.
Sonuç: ilk anda boş ekranı önlemek için initialData kullanın ve kullanıcıya hataları doğru şekilde göstermek için her zaman hasError'ı işleyin.
Hata 1: her üst öğe yeniden oluşturulduğunda yeni bir Stream oluşturmak. Stream, her yapıda yeni bir nesne oluşturan bir ifade aracılığıyla iletilirse, StreamBuilder eskisinden aboneliğini iptal eder ve yeni akışa abone olur, bu da sonsuz bir yeniden oluşturma döngüsüne neden olur. Çözüm: bir remembered değişkeni veya sabit Stream'li bir StatefulWidget kullanın.
Hata 2: hata işleme eksikliği. Bir Stream, controller.sink.addError aracılığıyla hatalar yayabilir ve builder snapshot.hasError'ı kontrol etmezse, kullanıcı boş bir ekran veya sonsuz yükleme görür. Çözüm: her zaman hasError'ı kontrol edin ve net bir mesaj görüntüleyin.
Hata 3: kapatılmamış bir StreamController nedeniyle bellek sızıntıları. Denetleyici dispose'da kapatılmazsa, akış var olmaya devam eder ve GC belleği serbest bırakmaz. Çözüm: dispose'da controller.close() çağırın ve son işlemler için done olayını dinleyin.
Hata 4: yavaş bir builder fonksiyonu ile StreamBuilder kullanımı. Builder her akış olayında çağrıldığından, içindeki ağır hesaplamalar kare düşüşlerine neden olur. Çözüm: hesaplamaları ayrı bir isolate'e taşıyın veya veri dönüşümü için Stream.map kullanın.
Sonuç: StreamBuilder güçlü ancak talepkar bir araçtır. Stream yaşam döngüsünü izleyin, hataları işleyin ve builder'da ağır işlemlerden kaçının.
Sıkça Sorulan Sorular
FutureBuilder tek bir asenkron sonuç için tasarlanmıştır: bir Future'e abone olur, bir değer alır ve sonlanır. StreamBuilder, zaman içinde birden çok değer yayabilen bir Stream'e abone olur ve her yeni olayda UI'yı yeniden oluşturur.
AsyncSnapshot, mevcut abonelik durumunu (connectionState), alınan son değeri (data) ve akış bir istisna yaydıysa bir hata nesnesini (error) içeren değişmez bir nesnedir.
Hatalar, builder fonksiyonunda snapshot.hasError ve snapshot.error özellikleri aracılığıyla işlenir. Akış sink.addError ile bir hata yayarsa, AsyncSnapshot hatayı alır ve builder uygun bir mesaj veya yedek UI görüntülemelidir.
Evet, Stream broadcast ise (StreamController.broadcast ile oluşturulmuşsa). Single-subscription Stream yalnızca bir aboneye izin verir. Bir akışı birden çok widget arasında paylaşmak için broadcast denetleyici veya BehaviourSubject ile rxdart paketini kullanın.
UI yeniden oluşturmalarını tetikleyecek olayları filtrelemek için buildWhen parametresini kullanın. Ayrıca StreamBuilder'a veri iletmeden önce filtrelemek için Stream.transformer veya Stream.where uygulayın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun