↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift

4 dk okuma #flutter
Dizi · 22/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. Neden "Kalıcı" Depolamaya İhtiyaç Var?
  2. Seviye 1: shared_preferences — Basit Anahtar-Değer Depolama
  3. Seviye 2: Hive — Hızlı, NoSQL Tarzı Yerel Veritabanı
  4. Seviye 3: sqflite/Drift — İlişkisel (SQL) Veritabanı
  5. Drift — Tip Güvenli SQL (Kod Üretimi ile)
  6. Üç Seviyeyi Karşılaştıralım
  7. Pratik Bir Karar Rehberi
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler
  10. 🏁 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

bash
flutter pub add shared_preferences
dart
import '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ı

bash
flutter pub add hive hive_flutter
dart
import '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ı

bash
flutter pub add sqflite
dart
import '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)

bash
flutter pub add drift sqlite3_flutter_libs
flutter pub add --dev drift_dev build_runner
dart
import '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_preferences ile 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.
  • [ ] Hive kurup basit bir "not" nesnesini (başlık + içerik) bir box'a kaydet, okuyup ekranda listele.
  • [ ] sqflite ile 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.