Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
Dizi · 19/61 Flutter Türkçe Tutorial
- Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
- Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
- Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
- Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
- Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
- Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
- Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
- Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
- Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
- Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
- Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
- Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
- Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
- Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
- Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
- Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
- Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
- Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
- Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
- Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
- Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
- Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
- Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
- Bölüm 6 — Konu 24: Provider Paketi
- Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
- Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
- Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
- Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
- Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
- Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
- Bölüm 7 — Konu 31: Deep Linking
- Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
- Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
- Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
- Bölüm 8 — Konu 35: Implicit Animasyonlar
- Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
- Bölüm 8 — Konu 37: Hero Animasyonları
- Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
- Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
- Bölüm 9 — Konu 40: Platform Channels
- Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
- Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
- Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
- Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
- Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
- Bölüm 10 — Konu 46: CI/CD
- Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
- Bölüm 10 — Konu 48: DevTools Profiling
- Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
- Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
- Bölüm 11 — Konu 51: Supabase Alternatifi
- Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
- Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
- Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
- Bölüm 12 — Konu 55: Flutter Web / Desktop
- Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
- Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
- Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
- Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
- Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
- Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
İçindekiler 9 başlık
- StreamBuilder — FutureBuilder'ın Kuzeni
- Temel Fark: builder, Kaç Kere Çağrılır?
- Neden Bu Kadar Değerli? — setState() ile Karşılaştırma
- Gerçek Dünya Senaryosu — Canlı Sayaç
- Stream Aynı Future'daki Gibi Tuzağa Dikkat
- Broadcast Stream ile Kullanım — Dart Bölüm 10'u Hatırlama
- FutureBuilder vs StreamBuilder — Ne Zaman Hangisi?
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bir önceki derste FutureBuilder'ı ("bir kerelik" asenkron veri için) öğrendik. Bu derste, Dart Bölüm 10'da öğrendiğimiz Stream ("zamanla, tekrar tekrar gelen veri") kavramının Flutter karşılığını işleyeceğiz.
StreamBuilder — FutureBuilder'ın Kuzeni
StreamBuilder<int>(
stream: sayacStream(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Hata: ${snapshot.error}');
}
return Text('Değer: ${snapshot.data}');
},
)Fark hemen görülüyor: StreamBuilder, future: yerine stream: parametresi alıyor — ama builder fonksiyonunun imzası birebir aynı (AsyncSnapshot<T> kullanıyor, Bölüm 5 Konu 18'i hatırla). Bu, Flutter'ın API tasarımında tutarlılığı ne kadar önemsediğini gösteriyor — bir kere FutureBuilder'ı öğrendiğinde, StreamBuilder'ı öğrenmek neredeyse ücretsiz oluyor.
Temel Fark: builder, Kaç Kere Çağrılır?
Dart Bölüm 10 Konu 47'de Stream'in zamanla birden fazla değer ürettiğini öğrenmiştik. StreamBuilder'ın builder fonksiyonu, bu yüzden her yeni veri geldiğinde tekrar çağrılır:
Stream<int> sayacStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}Bu stream'i StreamBuilder ile dinlediğinde, ekranda sırasıyla 1, 2, 3, 4, 5 görürsün — her yield gerçekleştiğinde, StreamBuilder otomatik olarak widget'ı yeniden çizer (Bölüm 2 Konu 6'da öğrendiğimiz setState()'e benzer bir mekanizmayı, framework senin için otomatik olarak yönetiyor).
Neden Bu Kadar Değerli? — setState() ile Karşılaştırma
StreamBuilder olmadan, aynı işi yapmak isteseydin:
// ❌ Elle yönetim — çok daha karmaşık
class SayacWidget extends StatefulWidget {
@override
State<SayacWidget> createState() => _SayacWidgetState();
}
class _SayacWidgetState extends State<SayacWidget> {
int? _deger;
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
_subscription = sayacStream().listen((veri) {
setState(() {
_deger = veri;
});
});
}
@override
void dispose() {
_subscription.cancel(); // Bölüm 5 Konu 20'de dispose'u derinlemesine göreceğiz
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('Değer: ${_deger ?? "Bekleniyor"}');
}
}Bu, çalışır ama çok daha fazla kod ve elle yönetilmesi gereken kaynaklar (_subscription.cancel()'ı unutursan, Dart Bölüm 10'da öğrendiğimiz stream kaynak sızıntısı riski) içeriyor. StreamBuilder, bunların hepsini otomatik olarak yapar — widget ekrandan kaldırıldığında, aboneliği kendisi iptal eder.
Gerçek Dünya Senaryosu — Canlı Sayaç
class CanliSayac extends StatelessWidget {
const CanliSayac({super.key});
Stream<int> _sayacStream() async* {
int sayi = 0;
while (true) {
await Future.delayed(Duration(seconds: 1));
yield sayi++;
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: _sayacStream(),
initialData: 0, // stream'den ilk veri gelene kadar gösterilecek değer
builder: (context, snapshot) {
return Text(
'${snapshot.data}',
style: TextStyle(fontSize: 48),
);
},
);
}
}initialData parametresi — Dart Bölüm 5'te öğrendiğimiz varsayılan parametre kavramına benzer bir mantıkla çalışır: stream henüz hiç veri üretmemişse (ilk yield'den önce), snapshot.data yerine bu başlangıç değeri kullanılır. Bu, FutureBuilder'da olmayan, StreamBuilder'a özgü kullanışlı bir parametredir — çünkü stream'in "hiç veri üretmeden bekleme" süresi genelde daha belirsiz olabilir.
Stream Aynı Future'daki Gibi Tuzağa Dikkat
Bölüm 5 Konu 18'de öğrendiğimiz kritik tuzağı hatırla — stream: parametresine build() içinde doğrudan bir fonksiyon çağrısı vermek, burada da aynı sorunu yaratır:
// ❌ TEHLİKELİ — her rebuild'de YENİ bir stream başlatılır
StreamBuilder<int>(
stream: _sayacStream(), // her build() çağrısında yeni bir stream!
builder: (context, snapshot) => Text('${snapshot.data}'),
)Doğru çözüm, aynı kalıp:
class CanliSayacDogru extends StatefulWidget {
@override
State<CanliSayacDogru> createState() => _CanliSayacDogruState();
}
class _CanliSayacDogruState extends State<CanliSayacDogru> {
late final Stream<int> _stream; // Bölüm 5 Konu 18'deki AYNI kalıp
@override
void initState() {
super.initState();
_stream = _sayacStream(); // sadece bir kere oluşturulur
}
Stream<int> _sayacStream() async* {
int sayi = 0;
while (true) {
await Future.delayed(Duration(seconds: 1));
yield sayi++;
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: _stream,
builder: (context, snapshot) => Text('${snapshot.data ?? 0}'),
);
}
}Broadcast Stream ile Kullanım — Dart Bölüm 10'u Hatırlama
Dart Bölüm 10 Konu 48'de öğrendiğimiz broadcast stream'i hatırlarsan — eğer aynı stream'i birden fazla StreamBuilder (farklı widget'larda) dinleyecekse, o stream'in broadcast olması gerekir:
class BildirimServisi {
final StreamController<String> _controller = StreamController<String>.broadcast();
Stream<String> get bildirimler => _controller.stream;
void bildirimGonder(String mesaj) {
_controller.add(mesaj);
}
}Bu, gerçek uygulamalarda birden fazla ekranın aynı veri kaynağını (örneğin bir "okunmamış mesaj sayısı" göstergesi, hem ana ekranda hem ayarlar ekranında) dinlemesi gerektiğinde kullanılan bir kalıptır — Bölüm 6'da state management konusuna geldiğimizde, bu kalıbın daha gelişmiş versiyonlarını (Provider, Riverpod gibi araçlarla) göreceğiz.
FutureBuilder vs StreamBuilder — Ne Zaman Hangisi?
| Senaryo | Kullan |
|---|---|
| Bir API'den tek bir veri çekmek | FutureBuilder |
| Gerçek zamanlı, sürekli güncellenen veri (sohbet mesajları, canlı skor, sensör verisi) | StreamBuilder |
| Firestore gibi bir veritabanından canlı veri dinlemek | StreamBuilder (bunu Bölüm 11'de Firebase konusunda göreceğiz) |
🎯 Bu Dersten Çıkarılması Gerekenler
StreamBuilder,FutureBuilderile aynıAsyncSnapshot<T>API'sini kullanır, amabuilder, stream her yeni veri ürettiğinde tekrar çağrılır.StreamBuilder, ellelisten()/setState()/cancel()yönetiminin yerine geçer — abonelik yönetimini otomatik yapar.initialData, stream ilk veriyi üretene kadar gösterilecek varsayılan değeri belirtir.stream:parametresinebuild()içinde doğrudan fonksiyon çağrısı vermek,FutureBuilder'daki gibi bir tuzaktır —initState()+late finalile çözülür.- Birden fazla widget aynı stream'i dinleyecekse, stream'in
broadcastolması gerekir (Dart Bölüm 10'u hatırla).
📝 Ödevler
- [ ]
Stream.periodicveyaasync*/yieldile her saniye artan bir sayaç gösterenStreamBuilderörneği yaz. - [ ]
initialDatakullanarak, stream ilk veriyi üretmeden önce gösterilecek bir başlangıç değeri belirle. - [ ] Bilerek
stream:parametresine doğrudan fonksiyon çağrısı vererek tuzağa düş, sonrainitState()+late finalile düzelt. - [ ] Broadcast bir
StreamControlleroluştur, aynı stream'i iki farklıStreamBuilderwidget'ında dinle. - [ ] Kendi cümlelerinle, "FutureBuilder ile StreamBuilder'ı ne zaman kullanmalıyım" sorusunu, Dart Bölüm 10'da öğrendiğin Future/Stream farkına dayanarak açıkla.
Sıradaki konu: Bölüm 5 — Konu 20: HTTP İstekleri (http / dio Paketleri)