Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
Dizi · 22/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
- Neden "Kalıcı" Depolamaya İhtiyaç Var?
- Seviye 1: shared_preferences — Basit Anahtar-Değer Depolama
- Seviye 2: Hive — Hızlı, NoSQL Tarzı Yerel Veritabanı
- Seviye 3: sqflite/Drift — İlişkisel (SQL) Veritabanı
- Drift — Tip Güvenli SQL (Kod Üretimi ile)
- Üç Seviyeyi Karşılaştıralım
- Pratik Bir Karar Rehberi
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
- 🏁 Bölüm 5 Tamamlandı!
Bölüm 5'in son konusundayız. Şimdiye kadar veriyi ağdan (HTTP ile) çektik — bu derste, veriyi cihazın kendisinde, kalıcı olarak saklamayı öğreneceğiz. Uygulama kapanıp yeniden açıldığında bile verinin kaybolmamasını sağlayan üç farklı yaklaşımı, artan karmaşıklık sırasına göre işleyeceğiz.
Neden "Kalıcı" Depolamaya İhtiyaç Var?
Dart Bölüm 13 Konu 62'de öğrendiğimiz bellek (RAM), uygulama kapandığında tamamen silinir. Bir StatefulWidget'ın state'i (Bölüm 2 Konu 6'yı hatırla), sadece uygulama çalışırken var olur. Kullanıcının bir tercihini (örneğin tema seçimi), oturum bilgisini, veya çektiği verileri kalıcı olarak saklamak istiyorsan, cihazın disk depolamasını kullanman gerekir.
Seviye 1: shared_preferences — Basit Anahtar-Değer Depolama
flutter pub add shared_preferencesimport 'package:shared_preferences/shared_preferences.dart';
Future<void> kullaniciAdiKaydet(String isim) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('kullanici_adi', isim);
}
Future<String?> kullaniciAdiOku() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('kullanici_adi');
}shared_preferences, Dart Bölüm 4'te öğrendiğimiz Map<String, dynamic> mantığına çok benzer — basit anahtar-değer çiftleri (String, int, bool, double, List<String>) saklar. getInstance(), Dart Bölüm 6'da öğrendiğimiz static metod kalıbının bir örneği — bu, aslında singleton deseninin (Dart Bölüm 6 Konu 27'de "factory constructor, var olan bir nesneyi döndürebilir" dediğimiz kavramın somut uygulaması) bir kullanımıdır.
Ne zaman kullanılır? Basit ayarlar (tema tercihi, "bir daha gösterme" seçeneği, giriş yapmış kullanıcının adı gibi) için idealdir. Büyük veya karmaşık veri için uygun değildir — çünkü arka planda, tüm veri tek bir dosyada, düz metin olarak saklanır, bu da büyük veri setlerinde yavaşlar.
Seviye 2: Hive — Hızlı, NoSQL Tarzı Yerel Veritabanı
flutter pub add hive hive_flutterimport 'package:hive_flutter/hive_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
await Hive.openBox('ayarlar');
runApp(MyApp());
}
// Yazma
Future<void> ayarKaydet(String anahtar, dynamic deger) async {
final box = Hive.box('ayarlar');
await box.put(anahtar, deger);
}
// Okuma
dynamic ayarOku(String anahtar) {
final box = Hive.box('ayarlar');
return box.get(anahtar);
}WidgetsFlutterBinding.ensureInitialized() — bu, Bölüm 1 Konu 1'de öğrendiğimiz Flutter'ın motor başlatma sürecine ait bir çağrıdır; main() içinde, runApp()'tan önce herhangi bir native platform işlemi (Hive gibi disk erişimi gerektiren kütüphaneler) çalıştırmadan önce gereklidir — Flutter'ın widget sistemini, native koda bağlamadan önce "hazırlar."
Hive'ın shared_preferences'tan farkı: Hive, Box adı verilen "kutulara" organize edilmiş, daha büyük ve karmaşık veri saklayabilir — sadece basit tipler değil, kendi tanımladığın class'ları bile (uygun bir "adapter" ile) saklayabilirsin. Ayrıca Hive, performans için optimize edilmiştir — Dart'ın kendisiyle (native olmayan) yazıldığı için, tüm platformlarda (özellikle web'de) tutarlı çalışır.
Seviye 3: sqflite/Drift — İlişkisel (SQL) Veritabanı
flutter pub add sqfliteimport 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> veriTabaniAc() async {
return openDatabase(
join(await getDatabasesPath(), 'uygulama.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE kullanicilar(id INTEGER PRIMARY KEY, isim TEXT, yas INTEGER)',
);
},
version: 1,
);
}
Future<void> kullaniciEkle(Database db, String isim, int yas) async {
await db.insert('kullanicilar', {'isim': isim, 'yas': yas});
}
Future<List<Map<String, dynamic>>> kullanicilariGetir(Database db) async {
return await db.query('kullanicilar');
}sqflite, SQLite (dünyada en yaygın kullanılan gömülü ilişkisel veritabanı motoru) için bir Flutter bağlayıcısıdır. Ham SQL sorgularını (CREATE TABLE, INSERT, SELECT) yazmanı gerektirir. Dart Bölüm 4'te öğrendiğimiz Map<String, dynamic> burada yine karşımıza çıkıyor — db.query(), sonucu bu formatta döndürür, tıpkı JSON'da olduğu gibi.
Drift — Tip Güvenli SQL (Kod Üretimi ile)
flutter pub add drift sqlite3_flutter_libs
flutter pub add --dev drift_dev build_runnerimport 'package:drift/drift.dart';
part 'veritabani.g.dart'; // Bölüm 5 Konu 21'i hatırla — code generation!
class Kullanicilar extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get isim => text()();
IntColumn get yas => integer()();
}
@DriftDatabase(tables: [Kullanicilar])
class VeriTabani extends _$VeriTabani {
VeriTabani() : super(_openConnection());
// ...
}Drift, sqflite'ın üzerine inşa edilmiş, Bölüm 5 Konu 21'de öğrendiğimiz kod üretimi (build_runner) kullanarak, ham SQL string'leri yazmadan, tip güvenli bir şekilde veritabanı sorguları yazmanı sağlar. Bu, tıpkı json_serializable'ın "elle JSON parse etmek yerine, tip güvenli, otomatik üretilen kod" felsefesinin, veritabanı sorgularına uygulanmış halidir.
Üç Seviyeyi Karşılaştıralım
shared_preferences |
Hive |
sqflite/Drift |
|
|---|---|---|---|
| Veri türü | Basit anahtar-değer | Kutulara organize, karmaşık nesneler | İlişkisel tablolar (satır/sütun) |
| Sorgu yeteneği | Yok (sadece anahtarla oku/yaz) | Sınırlı (anahtarla erişim) | Güçlü (SQL — filtreleme, sıralama, JOIN) |
| Performans (büyük veri) | Zayıf | İyi | Çok iyi |
| Öğrenme eğrisi | Çok düşük | Düşük | Orta-yüksek (özellikle ham SQL ile) |
| Ne zaman kullanılır | Basit ayarlar, tercihler | Orta boy veri, karmaşık nesneler | Büyük, ilişkisel veri (sipariş geçmişi, çoklu tablo) |
Pratik Bir Karar Rehberi
shared_preferences kullan, eğer:
- Sadece birkaç basit ayar saklıyorsan (tema, dil tercihi, "onboarding gösterildi mi" gibi bool bayraklar).
Hive kullan, eğer:
- Orta karmaşıklıkta veri saklıyorsan (kullanıcı profili, favori listesi) ve hızlı, basit bir API istiyorsan.
- Web dahil tüm platformlarda tutarlı performans istiyorsan.
sqflite/Drift kullan, eğer:
- Verilerin birbiriyle ilişkili olması gerekiyorsa (örneğin bir "sipariş" tablosu, bir "sipariş kalemleri" tablosuyla ilişkili).
- Karmaşık sorgular (filtreleme, sıralama, gruplama) yapman gerekiyorsa.
- Uygulaman çevrimdışı-öncelikli (offline-first) bir mimari kullanıyorsa ve büyük miktarda yapılandırılmış veri tutuyorsa.
🎯 Bu Dersten Çıkarılması Gerekenler
shared_preferences, basit anahtar-değer çiftlerini kalıcı olarak saklamanın en kolay yoludur — küçük ayarlar için idealdir.Hive, daha karmaşık nesneleri, kutulara organize ederek, yüksek performansla saklayan bir NoSQL tarzı çözümdür.sqflite, SQLite'a doğrudan erişim sağlar;Drift, bunun üzerine kod üretimiyle tip güvenli bir katman ekler (Bölüm 5 Konu 21'de öğrendiğimiz code generation kalıbı).- Seçim, verinin karmaşıklığına, ilişkisel olup olmadığına ve sorgu ihtiyaçlarına göre yapılmalıdır.
📝 Ödevler
- [ ]
shared_preferencesile bir kullanıcı tercihini (örn. tema seçimi) kaydet, uygulamayı kapatıp yeniden açtığında (hot restart ile test edilebilir) hatırlandığını doğrula. - [ ]
Hivekurup basit bir "not" nesnesini (başlık + içerik) bir box'a kaydet, okuyup ekranda listele. - [ ]
sqfliteile basit bir tablo oluştur, birkaç kayıt ekle,query()ile hepsini çek ve listele. - [ ] Üç yaklaşımı da (shared_preferences, Hive, sqflite) kullanarak aynı basit veriyi (örn. bir isim listesi) saklayıp okuyan üç ayrı küçük örnek yaz, hangi durumda hangisini seçeceğine karar ver.
- [ ] Kendi cümlelerinle, "neden bazı veriler için basit anahtar-değer depolama yeterli değil, SQL veritabanı gerekiyor" sorusunu, ilişkisel veri kavramıyla açıkla.
🏁 Bölüm 5 Tamamlandı!
Asenkron dünya ve veri yönetimini tamamladık: FutureBuilder, StreamBuilder, HTTP istekleri, JSON serialization ve yerel depolama. Artık uygulamanı gerçek bir API'ye bağlayabiliyor, veriyi tip güvenli modellere dönüştürebiliyor ve kalıcı olarak saklayabiliyorsun — bu, gerçek dünya uygulamalarının omurgasını oluşturan bir bilgi birikimi.
Sıradaki durak Bölüm 6 — State Management: Şimdiye kadar sadece setState() kullandık, ama uygulaman büyüdükçe bu yetersiz kalmaya başlar. InheritedWidget'ın temellerinden, Provider ve Riverpod gibi modern çözümlere kadar, büyük uygulamalarda state'i nasıl yöneteceğini öğreneceksin.