Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
Dizi · 23/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 8 başlık
- Sorun: setState() Sadece "Yerel"dir
- InheritedWidget — Widget Ağacı Boyunca Veri "Yayınlama"
- Nasıl Kullanılır?
- dependOnInheritedWidgetOfExactType — Otomatik Rebuild Kaydı
- InheritedModel — Seçici Güncelleme (İleri Bir Optimizasyon)
- Neden Bu Kadar Temel? — Provider/Riverpod'a Köprü
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bölüm 6'ya hoş geldin — Flutter'ın en çok tartışılan konusuna giriyoruz. Bu derste, Bölüm 4 Konu 16 ve 17'de "bunu Bölüm 8'de (o zaman Bölüm 8 olarak planlanmıştı, şimdi Bölüm 6) tam göreceğiz" dediğimiz Theme.of(context)/MediaQuery.of(context) mekanizmasının gerçek işleyişini açıklayacağız.
Sorun: setState() Sadece "Yerel"dir
Bölüm 2 Konu 6'da öğrendiğimiz setState(), sadece o widget'ın kendi State'ini günceller. Peki, birbirinden uzak iki widget'ın aynı veriyi paylaşması gerekiyorsa (örneğin bir "sepet" state'i, hem ürün listesi ekranında hem sepet ikonu göstergesinde)?
class UstEkran extends StatefulWidget {
@override
State<UstEkran> createState() => _UstEkranState();
}
class _UstEkranState extends State<UstEkran> {
int _sepetSayisi = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
UrunListesi(), // burada bir ürün eklendiğinde _sepetSayisi'nı artırmak istiyoruz
SepetIkonu(sayi: _sepetSayisi), // burada bu sayıyı GÖSTERMEK istiyoruz
],
);
}
}UrunListesi ve SepetIkonu, kardeş widget'lardır (aynı ebeveynin çocukları) — birbirlerinin State'ine doğrudan erişemezler. Naif bir çözüm, tüm veriyi en üstteki widget'ta tutup, her seviyede elle parametre olarak aşağı aktarmaktır ("prop drilling" — çok yaygın bir problem).
// ❌ "Prop drilling" — veri, her seviyeden elle geçirilmek zorunda
UrunListesi(sepeteEkle: (urun) { setState(() { _sepetSayisi++; }); })Küçük ağaçlarda bu çalışır, ama widget ağacı derinleştikçe (5-6 seviye), her ara widget'ın, kendisiyle hiç ilgisi olmayan bir veriyi sadece "aşağı aktarmak için" parametre olarak taşıması gerekir — bu, Bölüm 3 Konu 13'te öğrendiğimiz "tek sorumluluk" prensibini ihlal eder.
InheritedWidget — Widget Ağacı Boyunca Veri "Yayınlama"
class SepetSaglayici extends InheritedWidget {
final int sepetSayisi;
final Widget child;
const SepetSaglayici({
super.key,
required this.sepetSayisi,
required this.child,
}) : super(child: child);
static SepetSaglayici? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<SepetSaglayici>();
}
@override
bool updateShouldNotify(SepetSaglayici oldWidget) {
return sepetSayisi != oldWidget.sepetSayisi;
}
}InheritedWidget, Dart Bölüm 7'de öğrendiğimiz extends ile türetilen özel bir widget türüdür — Bölüm 2 Konu 5'te öğrendiğimiz normal widget'lardan farklı bir amaca hizmet eder: kendi altındaki (descendant) tüm widget'lara, ağaç boyunca "elle taşımadan" veri erişimi sağlamak.
Parça parça inceleyelim:
1. static SepetSaglayici? of(BuildContext context) — Bu, Theme.of(context)/MediaQuery.of(context)'in tam olarak nasıl çalıştığını gösteriyor! Bölüm 6 Konu 16'da "bu, InheritedWidget'a dayanır" demiştik — işte gerçek mekanizma bu: context.dependOnInheritedWidgetOfExactType<T>(), widget ağacında yukarı doğru giderek, en yakın SepetSaglayici'yi bulur.
2. updateShouldNotify(...) — Dart Bölüm 7'de öğrendiğimiz override edilen bir metod. Bu, "veri değiştiğinde, bana bağımlı olan widget'ları yeniden çizmeli miyim?" sorusunun cevabıdır. sepetSayisi != oldWidget.sepetSayisi, Dart Bölüm 3'te öğrendiğimiz != karşılaştırma operatörüyle, eski ve yeni değeri kıyaslıyor.
Nasıl Kullanılır?
class UstEkran extends StatefulWidget {
@override
State<UstEkran> createState() => _UstEkranState();
}
class _UstEkranState extends State<UstEkran> {
int _sepetSayisi = 0;
void _sepeteEkle() {
setState(() {
_sepetSayisi++;
});
}
@override
Widget build(BuildContext context) {
return SepetSaglayici(
sepetSayisi: _sepetSayisi,
child: Column(
children: [
ElevatedButton(onPressed: _sepeteEkle, child: Text('Sepete Ekle')),
SepetIkonu(), // artık parametre almasına GEREK YOK
],
),
);
}
}
class SepetIkonu extends StatelessWidget {
const SepetIkonu({super.key});
@override
Widget build(BuildContext context) {
final sepetSaglayici = SepetSaglayici.of(context)!; // context'ten DOĞRUDAN erişim
return Icon(Icons.shopping_cart);
// (Gerçek uygulamada sepetSaglayici.sepetSayisi ile bir rozet gösterirdik)
}
}Kritik gözlem: SepetIkonu, artık _sepetSayisi'yi hiçbir parametre almadan, doğrudan SepetSaglayici.of(context) ile alıyor — Bölüm ağacında ne kadar derinde olursa olsun, context'i olan her widget, bu veriye erişebilir.
dependOnInheritedWidgetOfExactType — Otomatik Rebuild Kaydı
Buradaki en önemli detay, sadece "veriye erişmek" değil — dependOnInheritedWidgetOfExactType<T>() çağrıldığında, Flutter, o widget'ı, bu InheritedWidget'a "bağımlı" olarak kaydeder. Bölüm 2 Konu 6'da öğrendiğimiz rebuild mekanizmasını hatırlarsan — SepetSaglayici'nin sepetSayisi'si değiştiğinde (ve updateShouldNotify true döndürdüğünde), bu veriye bağımlı olan HER widget otomatik olarak yeniden çizilir — sen elle setState() çağırmana gerek kalmadan!
Bu, Bölüm 4 Konu 16'da "Theme değiştiğinde, temayı kullanan her widget otomatik güncellenir" dediğimiz davranışın tam açıklamasıdır.
InheritedModel — Seçici Güncelleme (İleri Bir Optimizasyon)
InheritedWidget'ın bir sınırlaması var: updateShouldNotify true döndürdüğünde, ona bağımlı OLAN HER widget yeniden çizilir — veri birden fazla alt-parça içeriyorsa (örneğin bir "kullanıcı profili" hem isim hem yaş içeriyorsa), sadece isim değiştiğinde bile, yaşı kullanan widget'lar da gereksiz yere yeniden çizilir.
class ProfilModeli extends InheritedModel<String> {
final String isim;
final int yas;
// ...
@override
bool updateShouldNotifyDependent(ProfilModeli oldWidget, Set<String> dependencies) {
if (dependencies.contains('isim') && isim != oldWidget.isim) return true;
if (dependencies.contains('yas') && yas != oldWidget.yas) return true;
return false;
}
}InheritedModel<T>, Dart Bölüm 9'da öğrendiğimiz generic sınırlamayı kullanarak (burada T = String, "hangi alana bağımlı olduğunu" bir string ile belirtiyoruz), sadece kullanılan alt-parçaya bağımlı widget'ların güncellenmesini sağlar — bu, Bölüm 9'da (performans) derinlemesine işleyeceğimiz bir optimizasyon tekniğidir. Pratikte, InheritedModel'i doğrudan elle yazman nadirdir — modern state management araçları (Provider, Riverpod) bu tarz optimizasyonları senin için halleder.
Neden Bu Kadar Temel? — Provider/Riverpod'a Köprü
Önemli bir gerçek: Bölüm 6'nın ilerleyen konularında öğreneceğin Provider paketi, aslında arka planda InheritedWidget kullanır! Provider, InheritedWidget'ın kaynak kodunu elle yazma zahmetini ortadan kaldıran, üzerine inşa edilmiş kullanışlı bir katmandır. Bu, Bölüm 5 Konu 20'de dio'nun http'nin üzerine bir kolaylık katmanı olması gibi bir ilişki.
Bu dersi neden bu kadar derinlemesine işledik? Çünkü Provider/Riverpod'u "sihir" olarak değil, temelini anlayarak öğrenmen, ileride karşılaşacağın hataları çok daha kolay çözmeni sağlayacak — "neden bu widget güncellenmiyor" gibi sorularla karşılaştığında, artık arka planda InheritedWidget mekanizmasının nasıl çalıştığını bileceksin.
🎯 Bu Dersten Çıkarılması Gerekenler
setState(), sadece kendi State'ini günceller — kardeş/uzak widget'lar arasında veri paylaşmak için "prop drilling" gerekir, bu da tek sorumluluk prensibini ihlal eder.InheritedWidget, widget ağacı boyunca veri "yayınlamanın" temel Flutter mekanizmasıdır;of(context)static metodu,dependOnInheritedWidgetOfExactTypeile en yakın örneği bulur.dependOnInheritedWidgetOfExactType, sadece veri okumakla kalmaz, o widget'ı otomatik rebuild'e kaydeder — veri değiştiğinde bağımlı widget'lar kendiliğinden güncellenir.Theme.of(context)veMediaQuery.of(context), bu mekanizmanın gerçek kullanım örnekleridir.InheritedModel, seçici (sadece kullanılan alt-parçaya bağımlı) güncelleme sağlayan ileri bir optimizasyondur.- Provider ve Riverpod gibi modern araçlar, arka planda
InheritedWidget'ı kullanır — bu dersi anlamak, onları "sihir" olmaktan çıkarır.
📝 Ödevler
- [ ] Sadece
InheritedWidgetkullanarak, iki farklı (kardeş) widget arasında paylaşılan bir sayaç state'i kur. - [ ]
updateShouldNotify'ı bilerek her zamanfalsedöndürecek şekilde yaz, bağımlı widget'ların güncellenmediğini gözlemle; sonra doğru mantıkla düzelt. - [ ]
SepetSaglayici.of(context)çağrısını,InheritedWidgetolmayan bir yerde (örn. hiçSepetSaglayiciolmayan bir widget ağacında) yapmayı dene,nulldöndüğünü gözlemle (ve!ile zorladığında hatayı gör). - [ ] Kendi cümlelerinle, "Theme.of(context) çağrıldığında Flutter'ın arka planda ne yaptığını", bu derste öğrendiğin mekanizmayla açıkla.
- [ ] Kendi basit bir
InheritedWidget'ını (örn. bir "kullanıcı adı" paylaşan) yaz, en az 3 seviye derinlikte bir widget'tan bu veriye eriş.
Sıradaki konu: Bölüm 6 — Konu 24: Provider Paketi