Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
Dizi · 18/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
- Sorun: Asenkron Veri, Senkron UI
- FutureBuilder — Çözüm
- AsyncSnapshot — Future'ın "O Anki Durumunun" Fotoğrafı
- ConnectionState — Bir Enum, Dart Bölüm 7'yi Hatırla
- Gerçek Bir Örnek — Simüle Edilmiş API Çağrısı
- Kritik Bir Tuzak — future: Parametresini build() İçinde Doğrudan Çağırmak
- ?? ile Güvenli Varsayılan Değer
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bölüm 5'e hoş geldin — burada Dart Bölüm 10'da öğrendiğin Future/async/await bilgisi doğrudan işine yarayacak. Yeni bir kavram öğrenmiyorsun, var olan bilgini Flutter'ın UI dünyasına uyguluyorsun.
Sorun: Asenkron Veri, Senkron UI
Dart Bölüm 10'da, bir Future'ın sonucunun gelmesini beklerken, programın donmadığını öğrenmiştik. Ama Flutter'da bir problem var: build() metodu (Bölüm 2 Konu 5'i hatırla), senkron çalışmak zorundadır — build() içinde await kullanamazsın.
@override
Widget build(BuildContext context) {
final veri = await birApiCagrisi(); // ❌ HATA! build() async olamaz
return Text(veri);
}Neden olmaz? build(), Flutter'ın belirli bir anda, senkron olarak çağırdığı bir metoddur — Bölüm 1 Konu 1'de öğrendiğimiz RenderObject ağacının, her frame'de tutarlı bir şekilde güncellenmesi gerekir. Eğer build() "bekleyebilseydi", Flutter'ın ne zaman ekranı çizeceğini bilmesi imkansız hale gelirdi.
FutureBuilder — Çözüm
FutureBuilder<String>(
future: birApiCagrisi(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // yükleniyor
} else if (snapshot.hasError) {
return Text('Hata: ${snapshot.error}');
} else {
return Text('Veri: ${snapshot.data}');
}
},
)FutureBuilder, Bölüm 3 Konu 11'de öğrendiğimiz builder pattern'in bir başka uygulamasıdır — ama bu sefer, builder fonksiyonu birden fazla kez çağrılır: bir kere Future beklerken, bir kere daha Future tamamlandığında (Flutter, arka planda bunu senin için setState() benzeri bir mekanizmayla yönetir).
AsyncSnapshot — Future'ın "O Anki Durumunun" Fotoğrafı
Dart Bölüm 10'da bir Future'ın üç olası durumu olduğunu öğrenmiştik (henüz tamamlanmadı / başarıyla tamamlandı / hata ile tamamlandı). AsyncSnapshot<T>, Dart Bölüm 9'da öğrendiğimiz generic bir sınıf olarak, bu üç durumu tek bir nesnede kapsüller:
snapshot.connectionState // ConnectionState.none / waiting / active / done
snapshot.hasData // veri geldi mi? (bool)
snapshot.data // gelen veri (T?, nullable!)
snapshot.hasError // hata oluştu mu? (bool)
snapshot.error // oluşan hata (Object?, nullable!)Dikkat et: snapshot.data, nullable'dır (Dart Bölüm 2'yi hatırla) — çünkü Future henüz tamamlanmamışsa, henüz bir veri yok. Bu yüzden snapshot.data kullanmadan önce, connectionState veya hasData ile kontrol etmen gerekir — tıpkı Bölüm 2'de öğrendiğimiz null safety pratiklerinde olduğu gibi.
ConnectionState — Bir Enum, Dart Bölüm 7'yi Hatırla
enum ConnectionState { none, waiting, active, done }Bu, Dart Bölüm 7'de öğrendiğimiz enum'un, Flutter'ın kendi kütüphanesinde tanımlanmış bir örneğidir:
none: henüz bir future verilmedi.waiting: future çalışıyor, henüz tamamlanmadı.done: future tamamlandı (başarılı veya hatalı).
FutureBuilder ile en sık waiting ve done durumlarını kontrol edersin.
Gerçek Bir Örnek — Simüle Edilmiş API Çağrısı
class VeriEkrani extends StatelessWidget {
const VeriEkrani({super.key});
Future<String> _veriGetir() async {
await Future.delayed(Duration(seconds: 2)); // Dart Bölüm 10'u hatırla
return 'Sunucudan gelen veri';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Veri Ekranı')),
body: Center(
child: FutureBuilder<String>(
future: _veriGetir(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Bir hata oluştu: ${snapshot.error}');
}
return Text(snapshot.data!); // done durumunda, data artık null değil
},
),
),
);
}
}Bu kod çalıştığında: 2 saniye boyunca bir yükleme göstergesi (CircularProgressIndicator) görünür, sonra otomatik olarak "Sunucudan gelen veri" metnine geçer — kullanıcı hiçbir şey yapmadan.
Kritik Bir Tuzak — future: Parametresini build() İçinde Doğrudan Çağırmak
// ❌ TEHLİKELİ pratik
class VeriEkraniYanlis extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _veriGetir(), // HER rebuild'de YENİDEN çağrılır!
builder: (context, snapshot) { ... },
);
}
}Bölüm 2 Konu 6'da öğrendiğimiz build()'in her rebuild'de tekrar çalıştığını hatırlarsan — bu, ciddi bir soruna yol açıyor: eğer bu widget'ı içeren bir üst widget herhangi bir sebeple setState() çağırırsa (Bölüm 2'yi hatırla), build() tekrar çalışır, ve _veriGetir() her seferinde yeniden çağrılır — yani API isteği gereksiz yere tekrar tekrar atılır!
Doğru pratik — Future'ı StatefulWidget'ın state'inde saklamak:
class VeriEkraniDogru extends StatefulWidget {
const VeriEkraniDogru({super.key});
@override
State<VeriEkraniDogru> createState() => _VeriEkraniDogruState();
}
class _VeriEkraniDogruState extends State<VeriEkraniDogru> {
late final Future<String> _veriFuture; // late final — Dart Bölüm 8'i hatırla!
@override
void initState() {
super.initState();
_veriFuture = _veriGetir(); // SADECE BİR KERE çağrılır
}
Future<String> _veriGetir() async {
await Future.delayed(Duration(seconds: 2));
return 'Sunucudan gelen veri';
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _veriFuture, // her rebuild'de AYNI future kullanılıyor
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
return Text(snapshot.data ?? 'Veri yok');
},
);
}
}late final Future<String> _veriFuture — Dart Bölüm 8 Konu 40'ta öğrendiğimiz late'in lazy initialization dışındaki asıl kullanım senaryosunu görüyorsun: alan, constructor'da değil, initState() içinde (Bölüm 2 Konu 6'da kısaca gördüğümüz, bir kere çalışan metod) atanıyor, ama non-nullable kalıyor. initState(), sadece bir kere çağrıldığı için, _veriGetir() de sadece bir kere çalışıyor — build() ne kadar tekrar çalışırsa çalışsın, aynı Future nesnesi kullanılıyor.
?? ile Güvenli Varsayılan Değer
Text(snapshot.data ?? 'Veri yok')Dart Bölüm 2/3'te öğrendiğimiz ?? operatörü, burada çok yaygın bir kalıptır — snapshot.data nullable olduğu için, ! ile zorlamak yerine (riskli, Bölüm 2'yi hatırla), ?? ile güvenli bir varsayılan sağlamak daha iyi bir pratiktir.
🎯 Bu Dersten Çıkarılması Gerekenler
build()senkron çalışmak zorundadır,awaitkullanamaz —FutureBuilder, birFuture'ın durumuna göre farklı UI göstermeyi sağlar.AsyncSnapshot, bir Future'ın o anki durumunu (waiting/done, data, error) kapsülleyen generic bir sınıftır.snapshot.data/snapshot.errornullable'dır — kullanmadan önceconnectionStateveyahasData/hasErrorile kontrol edilmelidir.- Kritik tuzak:
future:parametresinebuild()içinde doğrudan bir fonksiyon çağrısı vermek, her rebuild'de gereksiz yeniden çağrılara yol açar —Future'ıinitState()içinde,late finalbir alanda saklamak doğru çözümdür.
📝 Ödevler
- [ ]
Future.delayedile sahte bir veri çeken,FutureBuilderile loading/error/success durumlarını yöneten bir ekran yaz. - [ ] Bilerek
future:parametresine doğrudan bir fonksiyon çağrısı ver, üst widget'ta birsetState()tetikleyerek API çağrısının tekrar tekrar yapıldığını (birprintile sayarak) kanıtla. - [ ] Aynı sorunu,
initState()+late finalkalıbıyla düzelt, artık sadece bir kere çağrıldığını doğrula. - [ ]
FutureBuilder'da bilerek bir hata fırlatan bir future kullan (throw Exception(...)),snapshot.hasErrorile bunu yakalayıp kullanıcıya göster. - [ ]
ConnectionState'in farklı durumlarını (waiting,done)printile takip ederek,FutureBuilder'ınbuilderfonksiyonunun kaç kez çağrıldığını gözlemle.
Sıradaki konu: Bölüm 5 — Konu 19: Stream, StreamBuilder