Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
Dizi · 47/64 Dart Türkçe Tutorial
- Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
- Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
- Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
- Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
- Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
- Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
- Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
- Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
- Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
- Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
- Bölüm 3 — Konu 11: `if / else if / else`
- Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
- Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
- Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
- Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
- Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
- Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
- Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
- Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
- Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
- Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
- Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
- Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
- Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
- Bölüm 5 — Konu 26: Recursion (Özyineleme)
- Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
- Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
- Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
- Bölüm 6 — Konu 30: Getter / Setter
- Bölüm 6 — Konu 31: Statik Üyeler (`static`)
- Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
- Bölüm 7 — Konu 33: Metod Override Etme, `@override`
- Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
- Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
- Bölüm 7 — Konu 36: Mixin (`with`)
- Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
- Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
- Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
- Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
- Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
- Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
- Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
- Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
- Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
- Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
- Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
- Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
- Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
- Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
- Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
- Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
- Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
- Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
- Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
- Bölüm 12 — Konu 56: Extension Methods
- Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
- Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
- Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
- Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
- Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
- Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
- Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
- Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
- Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
İçindekiler 10 başlık
- StreamController Nedir?
- StreamController'ın Parçaları
- Neden StreamController Kullanılır? — Gerçek Dünya Senaryosu
- Tek Dinleyicili (Single-Subscription) vs Broadcast Stream
- Broadcast Stream — Birden Fazla Dinleyici
- Broadcast Stream'in Önemli Bir Farkı — Geç Katılan Dinleyiciler
- close() — Kaynak Yönetimi
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
- 🏁 Bölüm 10 Tamamlandı!
Bölüm 10'un son konusundayız. Bir önceki derste async*/yield ile stream üretmeyi gördük — bu, "otomatik" bir stream oluşturma yoluydu. Bu derste, bir stream'e elle, istediğin an veri eklemeni sağlayan StreamController'ı işleyeceğiz.
StreamController Nedir?
import 'dart:async';
void main() {
StreamController<int> controller = StreamController<int>();
controller.stream.listen((veri) {
print("Alınan veri: $veri");
});
controller.add(1); // stream'e elle veri ekliyoruz
controller.add(2);
controller.add(3);
controller.close(); // stream'i kapatıyoruz
}Çıktı:
Alınan veri: 1
Alınan veri: 2
Alınan veri: 3StreamController, bir Stream'i "programatik olarak" (elle, istediğin zaman) besleyebileceğin bir araçtır. async*/yield ile otomatik akan bir stream'in aksine, burada sen ne zaman veri ekleneceğine karar veriyorsun — controller.add(...) çağırdığın her an, o değer stream'e "yayınlanır."
Not: dart:async kütüphanesini import etmemiz gerekti — StreamController, Dart'ın çekirdek (dart:core) kütüphanesinde değil, dart:async kütüphanesinde tanımlıdır. Bölüm 12'de kütüphaneler ve import'ları detaylı işleyeceğiz; şimdilik bazı ileri özelliklerin ayrı kütüphanelerden geldiğini bilmen yeterli.
StreamController'ın Parçaları
StreamController<int> controller = StreamController<int>();
controller.stream // dinlemek (listen) için kullanılan Stream nesnesi
controller.sink // veri eklemek için kullanılan "giriş noktası" (add ile aynı işi görür)
controller.add(5) // controller.sink.add(5) ile aynı etkiyi yapar, kısayoldur
controller.close() // stream'i kapatır, artık veri eklenemezcontroller.stream, dışarıya (dinleyicilere) açılan taraf; controller.add() (veya controller.sink.add()), içeriye veri "besleyen" taraf. Bunu bir su borusu gibi düşünebilirsin — add() ile bir uçtan su akıtırsın, .stream.listen() ile diğer uçtan suyu toplarsın.
Neden StreamController Kullanılır? — Gerçek Dünya Senaryosu
async*/yield, önceden bilinen, otomatik bir akış için uygundur (örneğin "her saniye bir sayı üret"). Ama bazen, stream'e ne zaman veri ekleneceği dışarıdan tetiklenen bir olaya bağlıdır — örneğin bir buton her tıklandığında, ya da bir sensörden veri her geldiğinde:
import 'dart:async';
class ButonTikayici {
final StreamController<int> _controller = StreamController<int>();
int _sayac = 0;
Stream<int> get tiklamaStream => _controller.stream;
void butonaTikla() {
_sayac++;
_controller.add(_sayac); // dışarıdan tetiklenen bir olayda veri ekleniyor
}
void kapat() {
_controller.close();
}
}
void main() {
ButonTikayici buton = ButonTikayici();
buton.tiklamaStream.listen((sayi) {
print("Tıklama sayısı: $sayi");
});
buton.butonaTikla(); // Tıklama sayısı: 1
buton.butonaTikla(); // Tıklama sayısı: 2
buton.butonaTikla(); // Tıklama sayısı: 3
buton.kapat();
}Bu, Flutter'da çok yaygın bir kalıptır — bir buton her tıklandığında, bir text field her değiştiğinde, veya bir state değişikliği her olduğunda, StreamController ile bu olayları "yayınlayabilirsin", ve uygulamanın farklı yerleri bu stream'i dinleyerek tepki verebilir.
Tek Dinleyicili (Single-Subscription) vs Broadcast Stream
Varsayılan olarak, bir StreamController'ın stream'i sadece bir kere, bir dinleyici tarafından dinlenebilir:
void main() {
StreamController<int> controller = StreamController<int>();
controller.stream.listen((veri) => print("Dinleyici 1: $veri"));
controller.stream.listen((veri) => print("Dinleyici 2: $veri")); // ❌ HATA!
controller.add(1);
}Bu kod hata verir — Bad state: Stream has already been listened to. Varsayılan stream'ler, tek kullanımlık kabul edilir (buna "single-subscription stream" denir) — genelde bir dosya okuma gibi, "baştan sona bir kere işlenecek" senaryolar için tasarlanmıştır.
Broadcast Stream — Birden Fazla Dinleyici
Eğer birden fazla dinleyicinin aynı veriyi almasını istiyorsan, StreamController.broadcast() kullanman gerekir:
void main() {
StreamController<int> controller = StreamController<int>.broadcast();
controller.stream.listen((veri) => print("Dinleyici 1: $veri"));
controller.stream.listen((veri) => print("Dinleyici 2: $veri")); // ✅ artık çalışır!
controller.add(1);
controller.add(2);
}Çıktı:
Dinleyici 1: 1
Dinleyici 2: 1
Dinleyici 1: 2
Dinleyici 2: 2broadcast(), tıpkı bir radyo yayını gibi çalışır — birden fazla "dinleyici" (subscriber) aynı anda aynı veriyi alabilir. Bu, örneğin bir uygulamanın birden fazla farklı ekranının, aynı "kullanıcı oturum durumu" stream'ini aynı anda dinlemesi gerektiğinde kullanışlıdır.
Broadcast Stream'in Önemli Bir Farkı — Geç Katılan Dinleyiciler
void main() {
StreamController<int> controller = StreamController<int>.broadcast();
controller.add(1); // henüz kimse dinlemiyor, bu veri KAYBOLUR
controller.stream.listen((veri) => print("Geç gelen dinleyici: $veri"));
controller.add(2); // bu veri alınır
}Çıktı:
Geç gelen dinleyici: 2Broadcast stream'lerde, bir dinleyici abone olmadan önce yayınlanan veriler, o dinleyici için kaybolur — tıpkı canlı bir radyo yayınına, yayın başladıktan sonra katılmak gibi, geçmiş yayını dinleyemezsin. Bu, single-subscription stream'lerden farklı bir davranıştır ve dikkat edilmesi gereken bir noktadır.
close() — Kaynak Yönetimi
void main() {
StreamController<int> controller = StreamController<int>();
controller.stream.listen((veri) => print(veri));
controller.add(1);
controller.close(); // artık add() çağrılamaz, kaynaklar serbest bırakılır
controller.add(2); // ❌ HATA! kapatılmış bir controller'a veri eklenemez
}Bir önceki derste sonsuz stream'ler için verdiğimiz "kaynak sızıntısı" uyarısı burada somutlaşıyor: bir StreamController artık kullanılmayacaksa, .close() çağrılarak kaynakların serbest bırakılması önemlidir. Flutter'da, bu genelde bir widget'ın dispose() metodunda yapılır (Flutter tutorial'ında bunu tam olarak göreceğiz) — bir widget ekrandan kaldırıldığında, kullandığı StreamController'ları kapatmak, bellek sızıntılarını önler.
🎯 Bu Dersten Çıkarılması Gerekenler
StreamController, bir stream'e elle, istediğin an veri eklemeni sağlar (add()ile); dışarıdan tetiklenen olaylar içinasync*/yield'e göre daha uygundur.- Varsayılan
StreamController, tek bir dinleyiciye izin verir (single-subscription); birden fazla dinleyici içinStreamController.broadcast()kullanılmalıdır. - Broadcast stream'lerde, bir dinleyici abone olmadan önce yayınlanan veriler o dinleyici için kaybolur.
.close(), bir controller'ın kaynaklarını serbest bırakır — artık kullanılmayacak controller'lar için önemlidir (Flutter'da geneldedispose()içinde yapılır).
📝 Ödevler
- [ ] Bir
StreamController<int>oluştur, elle birkaç değer ekle (.add()),.listen()ile dinle. - [ ] Aynı controller'ı iki farklı dinleyici ile dinlemeyi dene (broadcast olmadan), hatayı gözlemle; sonra
.broadcast()ile düzelt. - [ ] Broadcast bir stream'de, bir dinleyicinin geç abone olduğunda önceki verileri kaçırdığını kanıtlayan bir örnek yaz.
- [ ]
ButonTikayicibenzeri bir sınıf yaz — dışarıdan tetiklenen bir olayı (örn. bir mesaj gönderme fonksiyonu)StreamControllerile stream'e dönüştür. - [ ] Bir controller'ı
.close()ile kapat, sonra.add()çağırmayı dene, hatayı gözlemle ve nedenclose()'un önemli olduğunu yorumla.
🏁 Bölüm 10 Tamamlandı!
Asenkron programlamanın tüm temel araçlarını işledik: Future, async/await, .then()/.catchError(), Stream, async*/yield ve StreamController. Bu, Flutter'da API çağrıları yapmak, gerçek zamanlı veri dinlemek ve kullanıcı etkileşimlerini yönetmek için doğrudan kullanacağın bilgi birikimi.
Sıradaki durak Bölüm 11 — Koleksiyonlarda İleri Fonksiyonel Programlama: map, where, reduce, fold gibi metodlarla, koleksiyonları çok daha güçlü ve kısa bir şekilde işlemeyi öğreneceğiz.