Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
Dizi · 6/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 10 başlık
- StatelessWidget — "Hiç Değişmeyen" Widget'lar
- StatefulWidget — "Zamanla Değişebilen" Widget'lar
- İki Sınıflı Yapı — Neden?
- setState() — Rebuild Mekanizmasının Kalbi
- Rebuild Mekanizması — Adım Adım Ne Oluyor?
- setState()'in Kapsamı — Sadece O Widget mı, Tüm Ağaç mı?
- Ne Zaman StatelessWidget, Ne Zaman StatefulWidget?
- initState() — İlk Bakış (Detaylı Olarak Bölüm 4/5'te)
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bu, Flutter'ın en kritik konularından biri — burada öğrendiklerin, yazacağın hemen her uygulamada karşına çıkacak.
StatelessWidget — "Hiç Değişmeyen" Widget'lar
Bir önceki derste hep StatelessWidget kullandık. Bu, kendi başına hiçbir zaman değişmeyen bir widget türüdür — oluşturulduktan sonra, aldığı veriler (parametreler) hiç değişmez.
class SelamKarti extends StatelessWidget {
final String isim;
const SelamKarti({super.key, required this.isim});
@override
Widget build(BuildContext context) {
return Text('Merhaba, $isim!');
}
}SelamKarti("Ahmet") oluşturduğunda, bu widget her zaman "Merhaba, Ahmet!" gösterir. Eğer farklı bir isim göstermek istersen, yeni bir SelamKarti widget'ı oluşturman gerekir (örneğin SelamKarti("Ayşe")) — var olan widget'ı "güncelleyemezsin", çünkü Dart Bölüm 2'de öğrendiğimiz gibi, final isim alanı bir kere atanır, değişmez.
StatefulWidget — "Zamanla Değişebilen" Widget'lar
Peki, bir sayaç uygulamasında olduğu gibi, kullanıcı etkileşimiyle değişen bir değer (örneğin tıklama sayısı) göstermen gerekiyorsa? İşte burada StatefulWidget devreye girer.
class SayacWidget extends StatefulWidget {
const SayacWidget({super.key});
@override
State<SayacWidget> createState() => _SayacWidgetState();
}
class _SayacWidgetState extends State<SayacWidget> {
int _sayac = 0;
void _arttir() {
setState(() {
_sayac++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Sayaç: $_sayac'),
ElevatedButton(
onPressed: _arttir,
child: Text('Artır'),
),
],
);
}
}Bu, StatelessWidget'tan yapısal olarak çok farklı — burada iki ayrı class var. Şimdi bunu parça parça açalım.
İki Sınıflı Yapı — Neden?
1. SayacWidget (StatefulWidget'tan türeyen):
class SayacWidget extends StatefulWidget {
const SayacWidget({super.key});
@override
State<SayacWidget> createState() => _SayacWidgetState();
}Bu class'ın kendisi, hâlâ immutable'dır (Bölüm 2 Konu 5'i hatırla — const constructor, final alanlar olurdu eğer parametre olsaydı). SayacWidget'ın tek görevi, createState() metoduyla kendi state'ini oluşturan başka bir nesneyi işaret etmektir.
2. _SayacWidgetState (State'ten türeyen):
class _SayacWidgetState extends State<SayacWidget> {
int _sayac = 0; // BU alan DEĞİŞEBİLİR — final değil!
// ...
}İşte gerçek "değişebilir" veri burada yaşıyor — _sayac, final değil, var gibi normal bir alan (Dart Bölüm 2'yi hatırla). Bu class, Dart Bölüm 9'da öğrendiğimiz generic sınıflara bir örnek: State<SayacWidget>, "bu state, SayacWidget tipindeki bir widget'a ait" anlamına gelir.
Neden alt çizgi ile başlıyor (_SayacWidgetState)? Dart Bölüm 6 Konu 30'u hatırlarsan — _ öneki, bu class'ı dosyaya özel (private) yapar. Bu, Flutter'ın bir konvansiyonudur: State class'ları genelde private tutulur, çünkü dışarıdan doğrudan erişilmesine gerek yoktur.
setState() — Rebuild Mekanizmasının Kalbi
void _arttir() {
setState(() {
_sayac++;
});
}setState(), Flutter'a "state'im değişti, lütfen build()'i tekrar çağır" demenin yoludur. Bu, kritik bir kural: _sayac++; yazarak değeri değiştirmek tek başına yeterli değildir — eğer bunu setState() içine koymazsan, Dart'ın belleğinde değer değişir ama Flutter, ekranı güncellemesi gerektiğini bilmez!
void _arttirYanlisYontem() {
_sayac++; // ❌ değer değişti AMA Flutter bunu bilmiyor, ekran GÜNCELLENMEZ
}
void _arttirDogruYontem() {
setState(() { // ✅ Flutter'a "değişiklik oldu, build() çağır" diyoruz
_sayac++;
});
}Bu, Dart Bölüm 5'te öğrendiğimiz closure kavramının doğrudan bir uygulamasıdır — setState(), parametre olarak bir anonim fonksiyon (Dart Bölüm 5 Konu 24-25'i hatırla) alıyor, bu fonksiyonu çalıştırıyor (içindeki değişiklikleri uyguluyor), sonra Flutter'a "artık build()'i tekrar çağırabilirsin" sinyalini veriyor.
Rebuild Mekanizması — Adım Adım Ne Oluyor?
setState() çağrıldığında, arka planda şu adımlar gerçekleşiyor (Bölüm 1 Konu 1'de öğrendiğimiz üç katmanlı mimariyi hatırla):
setState()içindeki kod çalışır —_sayacdeğeri gerçekten değişir (bu, sıradan bir Dart değişken ataması).- Flutter, bu widget'ın (ve State'inin) "kirli" (dirty) olduğunu işaretler — yani "yeniden çizilmesi gerekiyor" der.
- Flutter'ın zamanlayıcısı (scheduler), bir sonraki "frame" (ekran yenileme döngüsü) sırasında, bu widget'ın
build()metodunu tekrar çağırır. build(), yeni bir widget ağacı döndürür (yeni_sayacdeğeriyle).- Bölüm 1 Konu 1'de öğrendiğimiz gibi, Flutter, eski ve yeni widget ağacını karşılaştırır (diffing) ve sadece gerçekten değişen RenderObject'leri günceller — tüm ekranı sıfırdan çizmez.
Bu son adım kritik: setState(), "her şeyi sıfırdan çiz" demek değildir — Flutter, akıllıca, sadece neyin değiştiğini bulup minimum işi yapar. Bu, Dart Bölüm 13 Konu 62'de öğrendiğimiz performans bilinçli tasarım felsefesinin somut bir örneği.
setState()'in Kapsamı — Sadece O Widget mı, Tüm Ağaç mı?
class _SayacWidgetState extends State<SayacWidget> {
int _sayac = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Sayaç: $_sayac'), // bu ve alt widget'lar YENİDEN OLUŞTURULUR
BuyukResimWidget(), // bu da rebuild olur, EĞER const değilse!
],
);
}
}Önemli detay: setState() çağrıldığında, o State'in build() metodu tamamen yeniden çalışır — bu, build() içinde tanımlanan tüm widget ağacının (o State'e ait kısmın) yeniden oluşturulacağı anlamına gelir. Eğer BuyukResimWidget() gibi, değişmeyen bir widget varsa, bunu const yaparak (Bölüm 2 Konu 5'i ve Dart Bölüm 13 Konu 62'yi hatırla), Flutter'ın bu widget'ı gereksiz yere yeniden oluşturmaktan kaçınmasını sağlayabilirsin — bunu Bölüm 9'da (performans optimizasyonu) tam olarak işleyeceğiz.
Ne Zaman StatelessWidget, Ne Zaman StatefulWidget?
StatelessWidget kullan, eğer:
- Widget, oluşturulduktan sonra hiç değişmeyecekse (sadece dışarıdan aldığı parametrelere göre görünür).
- Widget'ın kendi iç durumu (internal state) yoksa.
StatefulWidget kullan, eğer:
- Widget'ın, kullanıcı etkileşimi, zamanlayıcı, ağ cevabı gibi bir şeyle zamanla değişen bir verisi varsa.
- Örnekler: bir form alanının içeriği, bir animasyonun ilerleme durumu, bir API çağrısının sonucu (Bölüm 5'te göreceğiz).
Pratik bir kural: Eğer bir widget içinde _degisken = yeniDeger; yazman gerekiyorsa ve bu değişikliğin ekranda görünmesini istiyorsan, bu bir StatefulWidget olmalı — ve o değişikliği her zaman setState() içine sarmalısın.
initState() — İlk Bakış (Detaylı Olarak Bölüm 4/5'te)
class _SayacWidgetState extends State<SayacWidget> {
int _sayac = 0;
@override
void initState() {
super.initState(); // Dart Bölüm 7'de öğrendiğimiz super çağrısı — ÖNCE üst sınıfın davranışı çalışmalı
print('Widget ilk kez oluşturuldu');
}
// ...
}initState(), bir State nesnesi ilk kez oluşturulduğunda, bir kere çağrılan bir metoddur — Dart Bölüm 6'da öğrendiğimiz constructor mantığına benzer, ama Flutter'ın kendi yaşam döngüsüne özgü bir kavram. Bunu, Bölüm 5'te (asenkron veri çekme) ve Bölüm 8'de (state management) çok daha derinlemesine işleyeceğiz.
🎯 Bu Dersten Çıkarılması Gerekenler
StatelessWidget, oluşturulduktan sonra değişmeyen widget'lardır;StatefulWidget, zamanla değişebilen bir "state" nesnesine sahiptir.StatefulWidget, iki class'tan oluşur: widget'ın kendisi (immutable) veStatesınıfı (değişebilir veri burada tutulur).setState(), Flutter'a "state değişti,build()'i tekrar çağır" demenin tek doğru yoludur —setState()olmadan yapılan değişiklikler, veriyi değiştirir ama ekrana yansımaz.setState()çağrıldığında, Flutterbuild()'i tekrar çalıştırır, ama Widget/Element/RenderObject ayrımı sayesinde sadece gerçekten değişen kısımları günceller.- State class'ları genelde
_ile private tutulur (Flutter konvansiyonu).
📝 Ödevler
- [ ] Kendi basit bir sayaç
StatefulWidget'ı yaz (artı/eksi butonlu),setState()ile doğru şekilde güncelle. - [ ] Bilerek
setState()kullanmadan bir değişkeni değiştir, ekranın güncellenmediğini gözlemle; sonrasetState()ekleyip düzelt. - [ ]
initState()içine birprint()ekle, widget ilk oluştuğunda bunun bir kere çalıştığını, amasetState()sonrası tekrar çalışmadığını gözlemle. - [ ] Bir
StatefulWidgetiçinde, hem değişen (state'e bağlı) hem değişmeyen (const yapılabilecek) widget'lar bulundur — hangisinin hangisi olduğunu belirle. - [ ] Kendi cümlelerinle, "neden StatefulWidget iki ayrı sınıfa bölünmüş, tek sınıfta neden olmuyor" sorusunu, immutability (değişmezlik) kavramıyla ilişkilendirerek açıkla.
Sıradaki konu: Bölüm 2 — Konu 7: Temel Layout: Container, Row, Column, Stack, Padding, Align, Center