Bölüm 6 — Konu 24: Provider Paketi
Dizi · 24/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
- Paketi Ekleme
- ChangeNotifier — State'i Tanımlama
- ChangeNotifierProvider — State'i Widget Ağacına "Enjekte Etme"
- context.watch<T>() — Veriye Erişim ve Otomatik Rebuild
- context.read<T>() — Sadece Erişim, Rebuild Tetiklemeden
- context.select<T, R>() — İnce Ayarlı İzleme (Bölüm 6 Konu 23'teki InheritedModel'in Pratik Karşılığı)
- Birden Fazla Provider — MultiProvider
- Tam Bir Örnek
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ö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
flutter pub add providerChangeNotifier — State'i Tanımlama
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"
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
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
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:
// ❌ 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ığı)
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
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
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,ChangeNotifierProviderile widget ağacına ekle. - [ ]
context.watchkullanan bir widget ilecontext.readkullanan bir buton yaz, aralarındaki davranış farkını (rebuild tetikleyip tetiklemediğini) gözlemle. - [ ]
context.selectkullanarak, 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. - [ ]
MultiProviderile en az iki farklıChangeNotifiermodelini 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)