Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
Dizi · 50/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 12 başlık
- Neden "Backend-as-a-Service"?
- Firebase Authentication — Kullanıcı Girişi
- Cloud Firestore — Gerçek Zamanlı NoSQL Veritabanı
- Veri Yazma
- Tek Seferlik Okuma vs Gerçek Zamanlı Dinleme
- .where() — Sorgulama
- Firestore ile Model Sınıfları — Bölüm 5 Konu 21'e Geri Dönüş
- Firebase Storage — Dosya Yükleme
- Cloud Functions — "Sunucu Tarafı Mantık" İçin Son Parça
- Güvenlik Kuralları — Neden Client'tan Erişim Güvenli Olabiliyor?
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bölüm 11'e ("Backend & Yayınlama") hoş geldin. Bölüm 9 Konu 43'te Firebase'i sadece push notification için kullanmıştık — aslında Firebase, çok daha geniş bir "backend-as-a-service" (BaaS) platformu. Bu derste, bir uygulamanın kendi sunucusunu yazmadan ihtiyaç duyduğu dört temel şeyi (kullanıcı girişi, veritabanı, dosya depolama, sunucu mantığı) nasıl karşılayacağını göreceğiz.
Neden "Backend-as-a-Service"?
Bölüm 5 Konu 20'de http/dio ile kendi sunucuna istek atmayı öğrenmiştik — ama o sunucuyu birinin yazması gerekiyordu. Firebase, yaygın backend ihtiyaçlarının (kullanıcı kaydı, veri saklama, dosya yükleme) hazır, ölçeklenen bir altyapısını sunar — sen sıfırdan bir backend yazmadan, doğrudan Flutter'dan bu servislere bağlanırsın. Bu, özellikle MVP/tek kişilik projelerde (senin de önceliğin olan "hızlı ürün çıkarma" hedefine) çok değerlidir.
Firebase Authentication — Kullanıcı Girişi
flutter pub add firebase_authimport 'package:firebase_auth/firebase_auth.dart';
Future<UserCredential?> kayitOl(String eposta, String sifre) async {
try {
return await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: eposta,
password: sifre,
);
} on FirebaseAuthException catch (hata) {
print('Kayıt hatası: ${hata.code}'); // örn. 'email-already-in-use'
return null;
}
}
Future<UserCredential?> girisYap(String eposta, String sifre) async {
try {
return await FirebaseAuth.instance.signInWithEmailAndPassword(
email: eposta,
password: sifre,
);
} on FirebaseAuthException catch (hata) {
print('Giriş hatası: ${hata.code}'); // örn. 'wrong-password', 'user-not-found'
return null;
}
}FirebaseAuthException, Dart Bölüm 8'de öğrendiğimiz özel exception kavramının Firebase tarafındaki karşılığı — .code alanı, hangi hatanın olduğunu (yanlış şifre, zaten kayıtlı e-posta vb.) ayırt etmeni sağlar; bu, Konu 40'ta öğrendiğimiz PlatformException'ın mantığına çok benziyor.
Kullanıcının giriş durumunu takip etmek — bir Stream olarak:
Stream<User?> kullaniciDurumu = FirebaseAuth.instance.authStateChanges();Bölüm 5 Konu 19'da öğrendiğimiz Stream kavramının doğal bir kullanımı — kullanıcının giriş durumu değiştiğinde (giriş yaptı, çıkış yaptı, oturumu süresi doldu) bu stream otomatik olarak yeni bir değer yayınlar. Bunu, Bölüm 6'da öğrendiğimiz bir state management çözümüyle (Riverpod'da bir StreamProvider gibi) birleştirerek, uygulamanın her yerinde "kullanıcı giriş yapmış mı?" sorusuna anında cevap verebilirsin:
StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const AnaEkran(); // giriş yapılmış
}
return const GirisEkrani(); // giriş yapılmamış
},
)Bu kalıp, Bölüm 7 Konu 30'da go_router'ın redirect özelliğiyle birleştiğinde, "giriş yapılmadan belirli ekranlara erişilemez" kuralını merkezi olarak uygulamanın standart yoludur.
Cloud Firestore — Gerçek Zamanlı NoSQL Veritabanı
flutter pub add cloud_firestoreFirestore, veriyi koleksiyonlar (collections) ve belgeler (documents) halinde saklar — Dart Bölüm 4'te öğrendiğimiz Map<String, dynamic> yapısına çok benzer, iç içe geçebilen bir JSON-benzeri yapı:
kullanicilar (koleksiyon)
└─ kullanici_id_1 (belge)
isim: "Ahmet"
yas: 30
└─ kullanici_id_2 (belge)
isim: "Ayşe"
yas: 25Veri Yazma
await FirebaseFirestore.instance.collection('urunler').add({
'isim': 'Kablosuz Kulaklık',
'fiyat': 899.90,
'stokta': true,
});.add(...), Firestore'un otomatik olarak benzersiz bir ID ürettiği yeni bir belge oluşturur — Bölüm 8 Konu 37'de Hero için, Bölüm 10 Konu 47'de key için öğrendiğimiz "benzersiz kimlik" ihtiyacının, burada Firestore tarafından otomatik karşılandığını görüyorsun.
Tek Seferlik Okuma vs Gerçek Zamanlı Dinleme
// Tek seferlik okuma (Future)
final belge = await FirebaseFirestore.instance.collection('urunler').doc('urun123').get();
print(belge.data()?['isim']);
// GERÇEK ZAMANLI dinleme (Stream) — verinin GÜNCEL halini SÜREKLİ alırsın
Stream<QuerySnapshot> urunAkisi = FirebaseFirestore.instance
.collection('urunler')
.where('stokta', isEqualTo: true) // Dart Bölüm 5'te öğrendiğimiz .where() FİLTRELEMESİNE benzer
.snapshots();StreamBuilder<QuerySnapshot>(
stream: urunAkisi,
builder: (context, snapshot) {
if (!snapshot.hasData) return const CircularProgressIndicator();
final belgeler = snapshot.data!.docs;
return ListView.builder( // Bölüm 3 Konu 11 + Bölüm 10 Konu 49'u hatırla
itemCount: belgeler.length,
itemBuilder: (context, index) {
final urun = belgeler[index].data() as Map<String, dynamic>;
return ListTile(title: Text(urun['isim']));
},
);
},
)Firestore'un en güçlü özelliği burada: .snapshots(), bir Stream döner — veritabanında başka bir kullanıcı (ya da senin kendi uygulaman, başka bir cihazdan) bir belge eklerse/değiştirirse/silerse, bu stream otomatik olarak yeni veriyi yayınlar, sen hiçbir "yenile" butonuna ihtiyaç duymazsın. Bu, Bölüm 9 Konu 42'de geolocator'ın getPositionStream()'i ile aynı "sürekli akan veri" felsefesini, bu sefer bir veritabanı için uyguluyor.
.where() — Sorgulama
.where('stokta', isEqualTo: true), Dart Bölüm 5'te öğrendiğimiz koleksiyon .where() metoduna kavramsal olarak benziyor ama önemli bir fark var: Dart'ın .where()'i client'ta, zaten indirilmiş bir listeyi filtreler; Firestore'un .where()'i, filtrelemeyi sunucu tarafında yapar — yani sadece eşleşen belgeler cihazına indirilir, bu da hem veri tasarrufu hem hız sağlar.
Firestore ile Model Sınıfları — Bölüm 5 Konu 21'e Geri Dönüş
class Urun {
final String id;
final String isim;
final double fiyat;
Urun({required this.id, required this.isim, required this.fiyat});
factory Urun.fromFirestore(DocumentSnapshot belge) {
final veri = belge.data() as Map<String, dynamic>;
return Urun(id: belge.id, isim: veri['isim'], fiyat: veri['fiyat']);
}
Map<String, dynamic> toFirestore() => {'isim': isim, 'fiyat': fiyat};
}Bu, Bölüm 5 Konu 21'de öğrendiğimiz fromJson/toJson kalıbının birebir aynısı — sadece kaynak JSON yerine bir DocumentSnapshot. Bölüm 7'de öğrendiğimiz repository pattern ile birleştirirsen, Firestore, ProfilRepositoryImpl'in içindeki Dio çağrısının yerine geçen bir veri kaynağı olarak doğrudan slot içine oturur — domain katmanın (use case'ler) hangi veritabanını kullandığından habersiz kalmaya devam eder.
Firebase Storage — Dosya Yükleme
flutter pub add firebase_storageFuture<String> profilFotografiYukle(File dosya, String kullaniciId) async {
final referans = FirebaseStorage.instance.ref().child('profil_fotograflari/$kullaniciId.jpg');
await referans.putFile(dosya);
return await referans.getDownloadURL(); // yüklenen dosyanın herkese açık URL'si
}Bölüm 9 Konu 42'de image_picker ile cihazdan bir fotoğraf seçmeyi öğrenmiştik — Storage, o fotoğrafı buluta yüklemenin standart yoludur. getDownloadURL()'ün döndürdüğü URL'yi, Bölüm 3 Konu 12'de öğrendiğimiz Image.network(...) ile doğrudan kullanabilirsin.
Cloud Functions — "Sunucu Tarafı Mantık" İçin Son Parça
Auth, Firestore, Storage, client'tan doğrudan erişilebilir — ama bazı işlemlerin client'ta çalışmaması gerekir (örn. bir ödeme onaylama, hassas bir hesaplama, ya da bir kullanıcı silindiğinde ilişkili tüm verilerin otomatik temizlenmesi). Cloud Functions, JavaScript/TypeScript (ya da Python) ile yazılan, Firebase'in kendi sunucularında çalışan fonksiyonlardır — Flutter tarafından çağrılabilir ya da Firestore'daki bir değişikliğe tepki olarak otomatik tetiklenebilir.
// Flutter tarafı — bir Cloud Function'ı çağırma
final callable = FirebaseFunctions.instance.httpsCallable('siparisOnayla');
final sonuc = await callable.call({'siparisId': '123'});Not: Cloud Functions'ı yazmak TypeScript/JavaScript bilgisi gerektirir — senin kariyerinde de zaten planladığın (Flutter sonrası TypeScript) yol, tam olarak burada bir araya geliyor: Flutter (client) + Cloud Functions (TypeScript sunucu tarafı), aynı Firebase projesinde doğal bir kombinasyon oluşturur.
Güvenlik Kuralları — Neden Client'tan Erişim Güvenli Olabiliyor?
Firestore/Storage'a doğrudan client'tan erişmek ilk bakışta güvensiz görünebilir — "herkes benim veritabanıma yazabilir mi?" Cevap, Security Rules ile kontrol edilir (Firebase Console'da, ayrı bir dille yazılan kurallar):
match /urunler/{urunId} {
allow read: if true; // herkes okuyabilir
allow write: if request.auth != null; // sadece GİRİŞ YAPMIŞ kullanıcılar yazabilir
}Bu kurallar, her istek için sunucu tarafında (Firebase'in kendi altyapısında) kontrol edilir — client kodu ne yaparsa yapsın, kurallara uymayan bir işlem reddedilir. Bu, Bölüm 9 Konu 41'de izinlerin native tarafta doğrulanmasına biraz benzer bir fikir: client'a güvenmezsin, kuralları her zaman merkezi bir yerde uygularsın.
🎯 Bu Dersten Çıkarılması Gerekenler
- Firebase, kullanıcı girişi (Auth), veritabanı (Firestore), dosya depolama (Storage) ve sunucu mantığı (Functions) için kendi backend'ini yazmana gerek bırakmayan bir platform sunar.
authStateChanges(), kullanıcının giriş durumunu birStreamolarak sunar — Bölüm 7'dekigo_routerredirectile birleştiğinde merkezi bir giriş kontrolü sağlar.- Firestore,
.get()(tek seferlik) ve.snapshots()(gerçek zamanlıStream) ile okunabilir;.where()filtrelemesi sunucu tarafında çalışır. - Firestore verisi, Bölüm 5 Konu 21'deki
fromJson/toJsonkalıbıyla birebir aynı şekilde model sınıflarına dönüştürülür ve Bölüm 7'deki repository pattern'e doğal olarak oturur. - Storage, dosya yükleme/URL alma işini standartlaştırır; Cloud Functions, client'ta çalıştırılmaması gereken sunucu-taraflı mantığı barındırır.
- Security Rules, client'a güvenmeden, sunucu tarafında erişim kontrolü uygular.
📝 Ödevler
- [ ] Firebase Auth ile e-posta/şifre kayıt ve giriş akışı kur,
FirebaseAuthException'ın farklı.codedeğerlerini (yanlış şifre, zaten kayıtlı e-posta) kullanıcıya anlaşılır mesajlarla göster. - [ ]
authStateChanges()ile, giriş durumuna göreGirisEkrani/AnaEkranarasında otomatik geçiş yapan birStreamBuilderkur. - [ ] Firestore'a bir koleksiyon (örn. "notlar") için veri ekle,
.snapshots()ile gerçek zamanlı dinleyipListView.builderile göster; başka bir cihazdan (veya Firebase Console'dan) veri ekleyip anında güncellendiğini doğrula. - [ ] Firebase Storage'a bir resim yükle,
getDownloadURL()ile aldığın URL'yiImage.networkile göster. - [ ] Firebase Console'da basit bir Security Rule yaz (örn. "sadece giriş yapmış kullanıcılar yazabilir"), giriş yapmadan yazma denediğinde reddedildiğini doğrula.
Sıradaki konu: Bölüm 11 — Konu 51: Supabase Alternatifi