↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)

3 dk okuma #flutter
Dizi · 33/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: Nesneleri Nasıl "Bağlarız"?
  2. get_it — Basit Bir Servis Konumlandırıcı (Service Locator)
  3. Kullanım — Widget İçinde
  4. getIt<T>() ile Provider/Riverpod Arasındaki Fark
  5. injectable — Kod Üretimi ile Otomatikleştirme
  6. Neden Dependency Injection Bu Kadar Değerli? — Bölüm 7 Konu 32'ye Bağlantı
  7. 🎯 Bu Dersten Çıkarılması Gerekenler
  8. 📝 Ödevler

Sorun: Nesneleri Nasıl "Bağlarız"?

Bir önceki derste, UrunRepositoryImpl'in bir UrunVeriKaynagi'na, IndirimliUrunleriGetirUseCase'in de bir UrunRepository'ye ihtiyaç duyduğunu gördük. Bunları kim, nerede, nasıl oluşturup birbirine bağlayacak?

dart
// ❌ Her widget'ta elle oluşturmak — kod tekrarı, esneklik yok
class UrunListesiEkrani extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final veriKaynagi = UrunVeriKaynagi(http.Client());
    final repository = UrunRepositoryImpl(veriKaynagi);
    final useCase = IndirimliUrunleriGetirUseCase(repository);
    // ...
  }
}

Bu, her widget'ta tekrarlanan bir kurulum sürecidir — Bölüm 3 Konu 13'te öğrendiğimiz kod tekrarını önleme prensibini ihlal ediyor. Dependency Injection (bağımlılık enjeksiyonu), bu nesnelerin oluşturulmasını merkezi bir yerde yönetmenin ve ihtiyaç duyan yerlere "enjekte etmenin" yoludur.

get_it — Basit Bir Servis Konumlandırıcı (Service Locator)

bash
flutter pub add get_it
dart
import 'package:get_it/get_it.dart';

final getIt = GetIt.instance; // Bölüm 6 Konu 27'de gördüğümüz singleton kalıbına benzer

void bagimliliklariKur() {
  getIt.registerLazySingleton<http.Client>(() => http.Client());
  getIt.registerLazySingleton<UrunVeriKaynagi>(() => UrunVeriKaynagi(getIt<http.Client>()));
  getIt.registerLazySingleton<UrunRepository>(() => UrunRepositoryImpl(getIt<UrunVeriKaynagi>()));
  getIt.registerFactory<IndirimliUrunleriGetirUseCase>(() => IndirimliUrunleriGetirUseCase(getIt<UrunRepository>()));
}

void main() {
  bagimliliklariKur();
  runApp(MyApp());
}

GetIt.instance — Dart Bölüm 6 Konu 27'de öğrendiğimiz static getter/singleton kalıbının gerçek bir kullanımı — uygulama boyunca tek bir GetIt deposu vardır.

registerLazySingleton<T>(...) — Dart Bölüm 9'da öğrendiğimiz generic tip parametresi (<T>) ile, "bu tipte bir nesne istendiğinde, şu fonksiyonu çalıştırarak oluştur, ve bir daha oluşturma, aynı örneği (instance) tekrar kullan" diyoruz. "Lazy" kelimesi, Dart Bölüm 11 Konu 51'de öğrendiğimiz lazy evaluation kavramına birebir bağlanıyor — nesne, ilk gerçekten istendiğinde oluşturulur, uygulama başlarken değil.

registerFactory<T>(...) — registerLazySingleton'dan farklı olarak, her getIt<T>() çağrısında yeni bir örnek oluşturur — Dart Bölüm 6 Konu 27'de öğrendiğimiz factory constructor kavramına isim ve mantık olarak paralel.

Kullanım — Widget İçinde

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

  @override
  Widget build(BuildContext context) {
    final useCase = getIt<IndirimliUrunleriGetirUseCase>(); // TEK SATIR!
    // ...
  }
}

Artık, karmaşık kurulum zincirini (Client → VeriKaynagi → Repository → UseCase) her widget'ta tekrarlamak yerine, sadece getIt<T>() çağırarak hazır, bağlanmış bir nesne alıyorsun.

getIt<T>() ile Provider/Riverpod Arasındaki Fark

Bu, kafa karıştırıcı olabilir — Bölüm 6'da öğrendiğimiz Provider/Riverpod da bir tür "nesne sağlama" mekanizmasıydı. Kritik fark:

Provider/Riverpod get_it
Amaç State yönetimi — değişen, UI'a bağlı veri Bağımlılık yönetimi — servisler, repository'ler (genelde değişmeyen)
Widget ağacına bağımlı mı? Evet (BuildContext veya ref ile) Hayır — widget ağacının tamamen dışında da kullanılabilir
Rebuild tetikler mi? Evet (state değiştiğinde) Hayır — sadece nesne "sağlar", rebuild mantığı yoktur

Pratik olarak birlikte kullanılırlar: get_it, repository ve use case gibi "servisleri" enjekte eder; Provider/Riverpod, bu servisleri kullanan state'i (örneğin "şu an yüklenen ürün listesi") yönetir. Bölüm 7 Konu 32'deki UrunListesiEkrani örneğinde, ref.watch(indirimliUrunlerProvider) çağrısının arkasında, o provider'ın kendisi muhtemelen getIt<IndirimliUrunleriGetirUseCase>() ile use case'e erişiyor olurdu.

injectable — Kod Üretimi ile Otomatikleştirme

Bölüm 5 Konu 21 ve Bölüm 13'te (Dart) öğrendiğimiz kod üretimi (code generation) kavramını hatırlarsan — injectable, get_it'in kayıt sürecini (yukarıdaki bagimliliklariKur() fonksiyonunu elle yazma zahmetini), annotation'lar kullanarak otomatikleştirir.

bash
flutter pub add injectable
flutter pub add --dev injectable_generator build_runner
dart
import 'package:injectable/injectable.dart';

@lazySingleton // annotation ile işaretliyoruz
class UrunRepositoryImpl implements UrunRepository {
  final UrunVeriKaynagi _veriKaynagi;
  UrunRepositoryImpl(this._veriKaynagi); // injectable, bu parametreyi OTOMATİK ÇÖZER

  @override
  Future<List<Urun>> urunleriGetir() async {
    // ...
  }
}
bash
dart run build_runner build

Bu, bagimliliklariKur() içindeki elle yazılan getIt.registerLazySingleton<...>(...) satırlarının hepsini otomatik üretir — @lazySingleton annotation'ı gördüğünde, injectable, bu class'ın constructor'ındaki parametrelerine bakarak, gerekli bağımlılıkları otomatik olarak nasıl çözeceğini kendisi çıkarır (Dart Bölüm 6'da öğrendiğimiz constructor parametrelerinin statik analizi).

Neden Dependency Injection Bu Kadar Değerli? — Bölüm 7 Konu 32'ye Bağlantı

Bir önceki derste, Repository pattern'in test edilebilirlik sağladığını öğrenmiştik — "gerçek yerine sahte bir implementasyon kullanabilirsin" demiştik. Dependency Injection, bunu pratikte mümkün kılan mekanizmadır:

dart
// Test ortamında
void testOrtamiKur() {
  getIt.registerLazySingleton<UrunRepository>(() => SahteUrunRepository()); // GERÇEK yerine SAHTE
}

Testler çalışırken, getIt<UrunRepository>() çağrıldığında, gerçek API'ye bağlanan UrunRepositoryImpl yerine, sabit, kontrollü veri döndüren SahteUrunRepository (Dart Bölüm 7'de öğrendiğimiz implements ile, aynı sözleşmeye uyan bir başka class) kullanılır — kodun geri kalanı hiç değişmeden, sadece "hangi implementasyonun enjekte edildiği" değişiyor.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Dependency Injection, nesnelerin oluşturulma ve birbirine bağlanma sürecini merkezi bir yerde yönetir, kod tekrarını önler.
  • get_it, basit bir "servis konumlandırıcı" (service locator) — registerLazySingleton/registerFactory ile kayıt yapılır, getIt<T>() ile erişilir.
  • get_it, Provider/Riverpod'dan farklı olarak, widget ağacından bağımsızdır ve state değişince rebuild tetiklemez — genelde servisler/repository'ler için kullanılır.
  • injectable, annotation'lar ve kod üretimi (build_runner) ile, get_it kayıt sürecini otomatikleştirir.
  • Dependency Injection, Repository pattern'in test edilebilirlik avantajını pratikte mümkün kılan mekanizmadır — gerçek implementasyonları testlerde sahte olanlarla kolayca değiştirebilirsin.

📝 Ödevler

  • [ ] get_it ile bir servis sınıfını (örn. UrunVeriKaynagi) kaydet, getIt<T>() ile eriş, doğrudan new ile oluşturmaktan kaçın.
  • [ ] registerLazySingleton ile registerFactory arasındaki farkı, aynı sınıfı ikisiyle de kaydedip, birden fazla getIt<T>() çağrısıyla (identical() kullanarak, Dart Bölüm 13'ü hatırla) test et.
  • [ ] Bir Repository sözleşmesi (abstract class) için, hem gerçek hem sahte (test amaçlı) implementasyon yaz, get_it ile ikisi arasında geçiş yap.
  • [ ] injectable kurup, @lazySingleton annotation'ı ile basit bir class'ı otomatik kayıt ettir, üretilen kodu incele.
  • [ ] Kendi cümlelerinle, "get_it ile Provider/Riverpod'u ne zaman birlikte, ne için ayrı ayrı kullanırım" sorusunu açıkla.

Sıradaki konu: Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri