↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı

4 dk okuma #flutter
Dizi · 23/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 8 başlık
  1. Sorun: setState() Sadece "Yerel"dir
  2. InheritedWidget — Widget Ağacı Boyunca Veri "Yayınlama"
  3. Nasıl Kullanılır?
  4. dependOnInheritedWidgetOfExactType — Otomatik Rebuild Kaydı
  5. InheritedModel — Seçici Güncelleme (İleri Bir Optimizasyon)
  6. Neden Bu Kadar Temel? — Provider/Riverpod'a Köprü
  7. 🎯 Bu Dersten Çıkarılması Gerekenler
  8. 📝 Ö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)?

dart
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).

dart
// ❌ "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"

dart
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?

dart
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.

dart
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, dependOnInheritedWidgetOfExactType ile 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) ve MediaQuery.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 InheritedWidget kullanarak, iki farklı (kardeş) widget arasında paylaşılan bir sayaç state'i kur.
  • [ ] updateShouldNotify'ı bilerek her zaman false dö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ı, InheritedWidget olmayan bir yerde (örn. hiç SepetSaglayici olmayan bir widget ağacında) yapmayı dene, null dö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