Flutter & Mobil İle Yerel Veritabanı Kayıt İşlemi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce Flutter SDK'nızın güncel olduğundan emin olmalısınız. Yerel veritabanı işlemleri için sektör standardı olan sqflite paketini ve veri modellerini kolaylaştırmak için path paketini kullanacağız. Bu araçlar, cihazın dosya sistemine erişerek yapılandırılmış verileri saklamanıza olanak tanır.

Gerekli paketleri projenize eklemek için terminalinizde şu komutu çalıştırın:

flutter pub add sqflite path

Bu komut, pubspec.yaml dosyanıza gerekli bağımlılıkları ekleyecektir. Kurulum tamamlandıktan sonra, veritabanı işlemlerini yürütecek yardımcı bir sınıf (Helper Class) oluşturarak mimariyi temiz tutacağız.

Veritabanı Bağlantısı Nasıl Kurulur?

Veritabanı bağlantısı, uygulama yaşam döngüsü boyunca tek bir noktadan yönetilmelidir. Singleton tasarım deseni kullanarak veritabanı örneğine her yerden erişim sağlayabiliriz. Bu yaklaşım, gereksiz bağlantı açılmasını engeller ve bellek yönetimini optimize eder.

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class VeritabaniYardimcisi {
  static final VeritabaniYardimcisi instance = VeritabaniYardimcisi._init();
  static Database? _database;

  VeritabaniYardimcisi._init();

  Future get database async {
    if (_database != null) return _database!;
    _database = await _initDB('kullanici_verileri.db');
    return _database!;
  }

  Future _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);
    return await openDatabase(path, version: 1, onCreate: _createDB);
  }
}

Burada _initDB metodu, cihazın yerel depolama yolunu belirler ve veritabanı dosyasını oluşturur. onCreate parametresi, veritabanı ilk kez oluşturulduğunda çalışacak tablo şemasını tanımlamamıza olanak tanır.

Adım Adım Tablo Oluşturma ve Şema Tanımlama

Veritabanı bağlantısı kurulduktan sonra, verilerin saklanacağı tabloları tanımlamanız gerekir. SQL komutlarını kullanarak tablonuzu oluştururken veri tiplerine dikkat etmelisiniz. 2026 yılı itibarıyla, veri bütünlüğünü korumak için INTEGER PRIMARY KEY AUTOINCREMENT yapısını kullanmak en güvenli yoldur.

Future _createDB(Database db, int version) async {
  await db.execute('''
    CREATE TABLE kullanicilar (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      ad TEXT NOT NULL,
      yas INTEGER NOT NULL,
      kayitTarihi TEXT NOT NULL
    )
  ''');
}

Bu kod bloğu, kullanicilar adında bir tablo oluşturur. NOT NULL kısıtlaması, veritabanı tutarlılığını sağlamak için kritik öneme sahiptir. Tablo yapısını değiştirmek isterseniz, version numarasını artırarak onUpgrade metodunu kullanmanız gerektiğini unutmayın.

Veri Ekleme ve Güvenli Sorgulama

Veri eklerken SQL Injection saldırılarına karşı korunmak için doğrudan string birleştirme yapmamalısınız. Bunun yerine, sqflite paketinin sunduğu parametreli sorgu (prepared statements) yöntemini kullanın. Bu yöntem, verilerin temizlenerek veritabanına gönderilmesini sağlar.

Future kullaniciEkle(Map row) async {
  Database db = await VeritabaniYardimcisi.instance.database;
  return await db.insert('kullanicilar', row);
}

Veri eklerken Map yapısını kullanmak, kodun okunabilirliğini artırır. Örneğin; {'ad': 'Ahmet', 'yas': 25, 'kayitTarihi': '2026-05-20'} şeklinde bir veri göndererek güvenli bir ekleme işlemi gerçekleştirebilirsiniz.

Güvenlik Uyarısı: Yerel veritabanında hassas kullanıcı verileri (şifre, kimlik numarası vb.) saklıyorsanız, veritabanını mutlaka şifreleyin veya flutter_secure_storage gibi ek güvenlik katmanları kullanın. SQL sorgularında dışarıdan gelen verileri asla doğrudan string içine gömmeyin.

Verileri Listeleme ve Okuma Yöntemleri

Saklanan verileri uygulamada göstermek için query metodunu kullanırız. Bu metod, tablodaki tüm satırları veya belirli kriterlere uyan satırları getirmenizi sağlar. Büyük veri setlerinde performans kaybını önlemek için limit ve offset parametrelerini kullanmayı alışkanlık haline getirin.

Future tumKullanicilariGetir() async {
  Database db = await VeritabaniYardimcisi.instance.database;
  return await db.query('kullanicilar', orderBy: 'kayitTarihi DESC');
}

Bu metot, tüm kullanıcıları kayıt tarihine göre tersten sıralayarak getirir. Gelen veriler bir List olarak döner; bu verileri model sınıflarınıza dönüştürerek UI katmanında kolayca işleyebilirsiniz.

Yerel Veritabanı Yöntemleri Karşılaştırması

Yöntem Avantaj Dezavantaj
SQLite (sqflite) İlişkisel veri, karmaşık sorgular SQL bilgisi gerektirir
Shared Preferences Basit anahtar-değer çiftleri Büyük veriler için uygun değil
Hive (NoSQL) Çok yüksek performans İlişkisel sorgu desteği yok

Veri Güncelleme ve Silme İşlemleri

Uygulama içerisinde verilerin güncellenmesi veya silinmesi gerektiğinde, her zaman bir where parametresi kullanarak işlem yapılacak satırı spesifik olarak hedeflemelisiniz. Aksi takdirde, tüm tablonun silinmesi gibi ciddi hatalara yol açabilirsiniz.

Future kullaniciGuncelle(int id, Map row) async {
  Database db = await VeritabaniYardimcisi.instance.database;
  return await db.update('kullanicilar', row, where: 'id = ?', whereArgs: [id]);
}

Future kullaniciSil(int id) async {
  Database db = await VeritabaniYardimcisi.instance.database;
  return await db.delete('kullanicilar', where: 'id = ?', whereArgs: [id]);
}

whereArgs kullanımı, SQL Injection riskini ortadan kaldıran en güvenli yöntemdir. ? işareti, whereArgs içindeki değerle güvenli bir şekilde değiştirilir.

Sıkça Sorulan Sorular

Yerel veritabanı dosyasına bilgisayardan nasıl bakabilirim?

Android için Device File Explorer aracını kullanarak emülatördeki /data/data/paket_adiniz/databases/ dizinine giderek veritabanı dosyasını dışa aktarabilir ve SQLite Browser gibi araçlarla inceleyebilirsiniz.

Veritabanı boyutu sınırlı mıdır?

SQLite, cihazın disk kapasitesiyle sınırlıdır. Ancak uygulama performansını korumak için çok büyük verileri yerel veritabanı yerine bulut servislerinde tutmak daha sağlıklıdır.

Uygulama güncellendiğinde veritabanı silinir mi?

Hayır, onUpgrade metodunu doğru yapılandırdığınız sürece veriler korunur. Sürüm kontrolünü openDatabase içindeki version parametresi ile yönetmelisiniz.

Veritabanı işlemleri UI'ı dondurur mu?

Evet, veritabanı işlemleri ağırlaştığında ana iş parçacığını (Main Thread) dondurabilir. Bu yüzden tüm veritabanı işlemlerini async/await kullanarak asenkron olarak çalıştırmalısınız.

Şifreleme zorunlu mudur?

Eğer uygulamanızda kişisel veriler tutuyorsanız, KVKK ve GDPR uyumluluğu gereği verilerin şifrelenmesi bir "en iyi pratik" (best practice) olarak kabul edilir.

İleri Seviye Veritabanı Optimizasyonu ve Performans İpuçları

Uygulamanızdaki veri miktarı arttıkça, basit sorgular bile performans darboğazlarına neden olabilir. Yerel veritabanı işlemlerinde hız ve kaynak yönetimi, kullanıcı deneyimini doğrudan etkileyen en önemli faktörlerdir. Veritabanı performansını artırmak için aşağıdaki stratejileri uygulayabilirsiniz:

İndeksleme (Indexing) ile Sorgu Hızlandırma

Veritabanı tablolarınızda sıkça arama yaptığınız veya sıralama kullandığınız sütunlar için indeks oluşturmak, sorgu süresini milisaniyeler seviyesine indirir. İndeksler, veritabanının tüm tabloyu taramak yerine doğrudan ilgili kayda gitmesini sağlar.

// Tablo oluştururken veya sonradan indeks ekleme
await db.execute('''
  CREATE INDEX idx_user_email ON users (email)
''');

İşlem (Transaction) Kullanımı

Birden fazla kayıt ekleme veya güncelleme işlemini tek bir işlem bloğunda yapmak, veritabanı dosyasının her seferinde yeniden yazılmasını engeller. Bu yöntem, hem pil tüketimini azaltır hem de veri bütünlüğünü korur.

await database.transaction((txn) async {
  await txn.insert('tasks', {'title': 'Görev 1', 'status': 0});
  await txn.insert('tasks', {'title': 'Görev 2', 'status': 0});
});

Veritabanı Hata Ayıklama ve İzleme Stratejileri

Yerel veritabanı hataları genellikle çalışma zamanında (runtime) ortaya çıkar ve tespit edilmesi zordur. Uygulama geliştirme aşamasında veritabanı işlemlerini izlemek, olası veri kayıplarının önüne geçer.

SQL Sorgularını Loglama

Geliştirme aşamasında hangi sorguların çalıştığını görmek için sqflite kütüphanesinin sağladığı onConfigure özelliğini kullanabilirsiniz. Bu, hatalı sorguları tespit etmenize yardımcı olur.

final database = await openDatabase(
  path,
  onConfigure: (db) async {
    await db.execute('PRAGMA foreign_keys = ON');
  },
  onLog: (sql, args) {
    print('Çalışan Sorgu: $sql - Argümanlar: $args');
  },
);

Veritabanı Sürüm Yönetimi (Migration)

Uygulamanız güncellendiğinde veritabanı şemasını değiştirmek (yeni sütun eklemek vb.) için onUpgrade metodunu kullanmak zorunludur. Aksi takdirde, eski şemaya sahip kullanıcıların uygulaması çökecektir.

final database = await openDatabase(
  path,
  version: 2,
  onUpgrade: (db, oldVersion, newVersion) async {
    if (oldVersion < 2) {
      await db.execute('ALTER TABLE users ADD COLUMN phone TEXT');
    }
  },
);
Profesyonel İpucu: Veritabanı şemasını her zaman version numarası ile yönetin. onUpgrade bloğunda her sürüm değişikliği için ayrı bir if bloğu kullanarak, kullanıcının uygulama güncellemelerini atlaması durumunda bile tüm geçişlerin sırayla yapılmasını sağlayın.

Sonuç

Flutter & mobil ile yerel veritabanı kayıt işlemi, doğru mimariyle uygulandığında oldukça güçlü ve esnek bir yapı sunar. Bu rehberde öğrendiğiniz sqflite kullanımı ve güvenlik prensipleri, profesyonel bir mobil uygulama geliştirme sürecinin temelini oluşturur. Bir sonraki adım olarak, veritabanı işlemlerinizi Repository Pattern kullanarak UI katmanından tamamen ayırmayı ve veritabanı işlemlerini BLoC veya Provider gibi durum yönetimi kütüphaneleriyle birleştirmeyi deneyebilirsiniz.

Yasal Uyarı: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamına (production) geçmeden önce veritabanı yedekleme stratejilerinizi oluşturun ve kullanıcı verilerinin gizliliği konusunda gerekli yasal standartlara uygun hareket ettiğinizden emin olun.

Bu yazıya tepkinizi paylaşın:
Pelin Aydın

Kullanıcı deneyimi odaklı içerik stratejileri geliştiriyorum. Pratik bilgi arayan okuyucular için en hızlı ve etkili yöntemleri içeren rehberler hazırlıyorum.

Yorumlar (0)

Yorum Yaz