↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 6 — Konu 24: Provider Paketi

3 dk okuma #flutter
Dizi · 24/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 10 başlık
  1. Paketi Ekleme
  2. ChangeNotifier — State'i Tanımlama
  3. ChangeNotifierProvider — State'i Widget Ağacına "Enjekte Etme"
  4. context.watch<T>() — Veriye Erişim ve Otomatik Rebuild
  5. context.read<T>() — Sadece Erişim, Rebuild Tetiklemeden
  6. context.select<T, R>() — İnce Ayarlı İzleme (Bölüm 6 Konu 23'teki InheritedModel'in Pratik Karşılığı)
  7. Birden Fazla Provider — MultiProvider
  8. Tam Bir Örnek
  9. 🎯 Bu Dersten Çıkarılması Gerekenler
  10. 📝 Ödevler

Bir önceki derste InheritedWidget'ı elle yazmanın ne kadar detaylı olduğunu gördük (of() metodu, updateShouldNotify, vb.). Provider, bu süreci büyük ölçüde basitleştiren, en popüler Flutter state management paketlerinden biridir.

Paketi Ekleme

bash
flutter pub add provider

ChangeNotifier — State'i Tanımlama

dart
import 'package:flutter/foundation.dart';

class SepetModeli extends ChangeNotifier {
  int _sepetSayisi = 0;
  int get sepetSayisi => _sepetSayisi; // Bölüm 6 Konu 30'u hatırla — getter ile salt-okunur erişim

  void urunEkle() {
    _sepetSayisi++;
    notifyListeners(); // "bana bağımlı olan herkese haber ver, güncellenin!"
  }
}

ChangeNotifier, Dart Bölüm 7'de öğrendiğimiz extends ile türetilen, Flutter'ın kendi kütüphanesinde tanımlı bir sınıftır (dart:core'da değil, flutter/foundation.dart'ta — Dart Bölüm 12'de öğrendiğimiz import mekanizmasını hatırla). Bu class, "beni dinleyen widget'lara değişiklik bildirebilme" yeteneğini sağlar.

notifyListeners() — bu, Bölüm 6 Konu 23'te öğrendiğimiz updateShouldNotify + otomatik rebuild mekanizmasının, senin elle yazmana gerek kalmadan çalışan halidir. _sepetSayisi değiştiğinde notifyListeners() çağırıyorsun, ve Provider, arka planda, bu değişikliği dinleyen tüm widget'ları günceller.

ChangeNotifierProvider — State'i Widget Ağacına "Enjekte Etme"

dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => SepetModeli(),
      child: MyApp(),
    ),
  );
}

ChangeNotifierProvider, Bölüm 6 Konu 23'te elle yazdığımız SepetSaglayici widget'ının hazır, genel amaçlı bir versiyonudur — create: parametresi (Dart Bölüm 3 Konu 11'de öğrendiğimiz builder pattern), state nesnesini bir kere oluşturur ve tüm alt ağaca "yayınlar."

context.watch<T>() — Veriye Erişim ve Otomatik Rebuild

dart
class SepetIkonu extends StatelessWidget {
  const SepetIkonu({super.key});

  @override
  Widget build(BuildContext context) {
    final sepet = context.watch<SepetModeli>(); // Dart Bölüm 9'u hatırla — generic!
    return Badge(
      label: Text('${sepet.sepetSayisi}'),
      child: Icon(Icons.shopping_cart),
    );
  }
}

context.watch<SepetModeli>() — Dart Bölüm 9'da öğrendiğimiz generic tip parametresi (<SepetModeli>), Provider'a hangi state'e erişmek istediğini söylüyor. Bu, arka planda tam olarak Bölüm 6 Konu 23'te öğrendiğimiz dependOnInheritedWidgetOfExactType<T>() çağrısını kullanır — Provider, aslında kendi içinde bir InheritedWidget sarmalıyor.

watch, "bu değeri izle, değiştiğinde beni otomatik olarak yeniden çiz" demektir — az önce öğrendiğimiz SepetIkonu, SepetModeli.urunEkle() çağrıldığında (ve notifyListeners() tetiklendiğinde), otomatik olarak yeniden çizilir.

context.read<T>() — Sadece Erişim, Rebuild Tetiklemeden

dart
ElevatedButton(
  onPressed: () {
    context.read<SepetModeli>().urunEkle(); // SADECE metod çağırmak için
  },
  child: Text('Sepete Ekle'),
)

read ile watch arasındaki kritik fark: watch, o widget'ı değişikliklere bağımlı yapar (her değişiklikte rebuild). read, sadece o anki değere/metoda erişim sağlar, rebuild tetiklemez. Bir onPressed callback'i içinde (Dart Bölüm 5'te öğrendiğimiz anonim fonksiyon), genelde read kullanılır — çünkü bir buton, sepet sayısı değiştiğinde kendisinin yeniden çizilmesine gerek yoktur, sadece metodu tetiklemek ister.

Yanlış kullanım örneği — performans sorunu yaratır:

dart
// ❌ Kötü pratik — build() içinde watch kullanıp sadece bir metod çağırmak
onPressed: () {
  context.watch<SepetModeli>().urunEkle(); // ❌ HATA! watch, build() dışında kullanılamaz zaten
}

Aslında Provider, bu hatayı çalışma zamanında yakalar — watch, sadece build() metodunun içinde (rebuild mantığı gerektiren bağlamda) kullanılabilir.

context.select<T, R>() — İnce Ayarlı İzleme (Bölüm 6 Konu 23'teki InheritedModel'in Pratik Karşılığı)

dart
class SepetSayisiGostergesi extends StatelessWidget {
  const SepetSayisiGostergesi({super.key});

  @override
  Widget build(BuildContext context) {
    final sayi = context.select<SepetModeli, int>((model) => model.sepetSayisi);
    return Text('$sayi');
  }
}

Bölüm 6 Konu 23'te öğrendiğimiz InheritedModel'in "sadece kullanılan alt-parçaya bağımlı olma" optimizasyonunu hatırlarsan — select, tam olarak bunu Provider ile kolayca yapmanı sağlıyor. Eğer SepetModeli'nin başka bir alanı (örneğin bir indirimOrani) değişirse ama sepetSayisi değişmezse, select kullanan bu widget yeniden çizilmez — sadece sepetSayisi'ye gerçekten bağımlı olduğu için, gereksiz rebuild'lerden kaçınır.

Birden Fazla Provider — MultiProvider

dart
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => SepetModeli()),
        ChangeNotifierProvider(create: (context) => KullaniciModeli()),
      ],
      child: MyApp(),
    ),
  );
}

MultiProvider, Dart Bölüm 4'te öğrendiğimiz List yapısını kullanarak (providers: [...]), birden fazla state kaynağını tek bir yerde tanımlamanı sağlar — gerçek uygulamalarda, genelde birden fazla ChangeNotifier (kullanıcı, sepet, ayarlar gibi) aynı anda yönetilir.

Tam Bir Örnek

dart
class SepetModeli extends ChangeNotifier {
  final List<String> _urunler = [];
  List<String> get urunler => List.unmodifiable(_urunler); // Dart Bölüm 4'ü hatırla!

  void ekle(String urun) {
    _urunler.add(urun);
    notifyListeners();
  }
}

class SepetEkrani extends StatelessWidget {
  const SepetEkrani({super.key});

  @override
  Widget build(BuildContext context) {
    final sepet = context.watch<SepetModeli>();
    return Scaffold(
      appBar: AppBar(title: Text('Sepetim (${sepet.urunler.length})')),
      body: ListView.builder(
        itemCount: sepet.urunler.length,
        itemBuilder: (context, index) => ListTile(title: Text(sepet.urunler[index])),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<SepetModeli>().ekle('Yeni Ürün ${sepet.urunler.length + 1}'),
        child: Icon(Icons.add),
      ),
    );
  }
}

List.unmodifiable(_urunler) — Dart Bölüm 4 Konu 16'yı hatırlıyor musun? Bu, dışarıya salt-okunur bir liste sunmanın yoludur — SepetModeli'nin dışındaki kod, urunler listesine doğrudan eleman ekleyemez, sadece ekle() metodu üzerinden, kontrollü bir şekilde değiştirebilir. Bu, Bölüm 6 Konu 30'da (Dart) öğrendiğimiz encapsulation prensibinin, state management'ta kritik bir uygulamasıdır.


🎯 Bu Dersten Çıkarılması Gerekenler

  • ChangeNotifier, notifyListeners() ile bağımlı widget'lara değişiklik bildiren bir sınıftır — InheritedWidget'ın elle yazılmasının otomatikleştirilmiş halidir.
  • ChangeNotifierProvider/MultiProvider, state nesnelerini widget ağacına "enjekte eder."
  • context.watch<T>(), veriye erişir VE değişince otomatik rebuild tetikler; context.read<T>(), sadece erişir, rebuild tetiklemez (genelde callback'lerde kullanılır).
  • context.select<T, R>(), sadece belirli bir alt-alana bağımlı olarak, gereksiz rebuild'lerden kaçınmayı sağlar (InheritedModel'in pratik karşılığı).
  • List.unmodifiable, state modelinin içindeki koleksiyonları dışarıya salt-okunur sunmanın, encapsulation'ı koruyan bir yoludur.

📝 Ödevler

  • [ ] ChangeNotifier'dan türeyen basit bir sayaç modeli yaz, ChangeNotifierProvider ile widget ağacına ekle.
  • [ ] context.watch kullanan bir widget ile context.read kullanan bir buton yaz, aralarındaki davranış farkını (rebuild tetikleyip tetiklemediğini) gözlemle.
  • [ ] context.select kullanarak, modelin sadece belirli bir alanına bağımlı bir widget yaz; modelin başka bir alanını değiştirip bu widget'ın rebuild olmadığını kanıtla.
  • [ ] MultiProvider ile en az iki farklı ChangeNotifier modelini aynı anda yönet.
  • [ ] Konu 24'teki "SepetEkrani" örneğine benzer, kendi basit bir alışveriş sepeti uygulaması yaz (ürün ekleme + liste gösterme).

Sıradaki konu: Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)