Arkadaşlar merhaba.Ben bu zamana kadar hep database kısmına önyargı ile yaklaştım. Bana çok kompleks bir konu gibi gelmiştir ve sanki bu konuyu bir türlü kafam almıyor ama eninda sonunda yine karşıma çıktı fakat nasıl yapacağımı bilmiyorum.
Ben bir yapı kurmak istiyorum. Mesela, ilk sütunum name ,ikinci sütunum surname , ve üçüncü sütunum randomValue olsun.
Ben textfielddan name ve surname i alacağım eğer database de önceden böyle bir isim varsa ürettiğim rastgele sayıyı getireceğim. Yoksa bu isim soyisimle birlikte rastgele sayı üretip database e ekleyeceğim. Böyle bir yapıyı nasıl kurabilirim?

singleChildScrollView
Size SqfLite kullandığım bir helper dosyasını gönderiyorum. Okunaklı ve basit olmasını sağlamaya çalıştım ama takıldığınız yer olursa sorabilirsiniz. Helper singleton olarak tasarlandı.

sqflite: ^1.3.0+1
path_provider: ^1.6.7

Kullanımı

FoodDatabase db = FoodDatabase();
Food iFood =Food(name: "Salata",recipe: "Doğra karıştır",ingredients: "Domates biber soğan");
db.insertFood(iFood);
List<Food> foods = await db.allFoods();
Food aFood = await db.getFood(1);
Food aFood2 = await db.getFoodbyName("Mercimek Çorbası");
aFood2.name="Lahmacun";
db.foodUpdate(aFood2);

import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:step_meter/Models/Food.dart';
class FoodDatabase {
  static FoodDatabase _databaseHelper;
  static Database _database;

  //Sutun adları string olarak tanımlanır
  String _foodTableName = 'foods';
  String _id = 'id';
  String _name = 'name';
  String _calorie = 'calorie';
  String _ingredients = "ingredients";
  String _recipe = "recipe";
  String _bakedTime = "bakedTime";
  String _fav = "fav";

  FoodDatabase._internal();

  factory FoodDatabase() {
    if (_databaseHelper == null) {
      //print("Foods DATA BASE HELPER NULL, OLUSTURULACAK");
      _databaseHelper = FoodDatabase._internal();
      return _databaseHelper;
    } else {
      //print("Foods DATA BASE HELPER NULL DEGIL");
      return _databaseHelper;
    }
  }

  Future<Database> _getDatabase() async {
    if (_database == null) {
      //print("Foods DATA BASE NESNESI NULL, OLUSTURULACAK");
      _database = await _initializeDatabase();
      return _database;
    } else {
      //print("Foods DATA BASE NESNESI NULL DEĞİL");
      return _database;
    }
  }

  Directory klasor;
  String sd;
  _initializeDatabase() async {
    klasor = await getExternalStorageDirectory(); //Cihaz hafızasında uygulamanın kullandığı dosya yolu
    sd = klasor.parent.parent.parent.parent.path;//Cihazın ana dizini
    String path = join(sd, "MyDirectory", "Foods.db");//Ana dizinde MyDirectory klasörü

    if (!await databaseExists(path)) {
      try {
        await Directory(dirname(path)).create(recursive: true);// klasör yoksa oluşturur.
      } catch (_) {}
     
    }

    //print("Olusan veritabanının tam yolu : $path");
    var foodsDB = await openDatabase(path, version: 1, onCreate: _createDB);//db dosyası varsa açar. Yoksa _createDb tetiklenir ve veritabanı oluşturulur.
    return foodsDB;
  }

  Future _createDB(Database db, int version) async {
    //print("CREATE DB METHODU CALISTI TABLO OLUSTURULACAK");
    await db.execute(
        "CREATE TABLE $_foodTableName ($_id INTEGER PRIMARY KEY AUTOINCREMENT,$_name TEXT, $_ingredients TEXT, $_calorie TEXT ,$_recipe TEXT , $_bakedTime TEXT , $_fav INTEGER)");
  //String veri tipleri TEXT olarak belirtilir. Sqflite Bool desteklemez. Bu sebeple INTEGER kullanılır.
}

  Future<int> insertFood(Food food) async {
    var db = await _getDatabase();
    var sonuc = await db.insert(_foodTableName, food.toMap());
    return sonuc;
  }

  Future<List<Food>> allFoods() async {
    var db = await _getDatabase();
    var sonuc = await db.query(_foodTableName, orderBy: '$_name');
    List<Food> foodList = sonuc.map((foodMap) => Food.fromMap(foodMap)).toList();
    return foodList;
  }

  Future<List<Food>> favoriteFoods() async {
    var db = await _getDatabase();
    var sonuc = await db.query(_foodTableName,
        orderBy: '$_name', where: '$_fav = ?', whereArgs: [1]);
    List<Food> foodList = sonuc.map((e) => Food.fromMap(e)).toList();
    return foodList;
  }
  
  Future<Food> getFood(int id) async {
    var db = await _getDatabase();

    var sonuc = await db.query(_foodTableName,
        orderBy: '$_name', where: '$_id = ?', whereArgs: [id]);
    var aFood = Food.fromMap(sonuc.first);
    return aFood;
  }
  Future<Food> getFoodbyName(String name) async {
    var db = await _getDatabase();

    var sonuc = await db.query(_foodTableName,
        orderBy: '$_name', where: '$_name = ?', whereArgs: [name]);
    var aFood = Food.fromMap(sonuc.first);
    return aFood;
  }


  Future<int> foodUpdate(Food food) async {
    var db = await _getDatabase();
    var sonuc = db.update(_foodTableName, food.toMap(),
        where: '$_id = ?', whereArgs: [food.id]);
    return sonuc;
  }
  Future<int> foodDelete(int id) async {
    var db = await _getDatabase();
    var sonuc = db.delete(_foodTableName, where: '$_id = ?', whereArgs: [id]);
    return sonuc;
  }
}

Bu da Food Class

class Food {
  int id;
  String name;
  String calorie;
  String ingredients;
  String recipe;
  String bakedTime;
  bool fav;
  Food(
      {this.id,
      this.name,
      this.calorie,
      this.ingredients,
      this.recipe,
      this.bakedTime,
      this.fav});

  Map<String, dynamic> toMap() {
    return {
      'calorie': calorie,
      'name': name,
      'ingredients': ingredients,
      'recipe': recipe,
      'bakedTime': bakedTime,
      'fav': fav ? 1 : 0 //sqflite boo desteklemediği için int olarak kaydediyor ve okuyoruz.
    };
  }

  Food.fromMap(Map<String, dynamic> map)
      : id = map['id'] ?? 0,
        calorie = map['calorie'] ?? "",
        name = map['name'] ?? '~Adsız~',
        ingredients = map['ingredients'] ?? "",
        recipe = map['recipe'] ?? "",
        bakedTime = map['bakedTime'],
        fav = map['fav'] == 1 ? true : false; //sqflite bool desteklemediği için 1-0 olarak kaydettiğimiz veriyi tekrar bool olarak parse ediyoruz.
}

HseyinAkkaya
Hocam tekrardan rahatsız ediyorum kusura bakmayın ama çıldırmak üzereyim. Hata olmaması lazım hata veriyo buralar:

Bu product class

class Product{

  Product({this.id,this.name,this.description,this.unitPrice});
  
  int id;
  String name;
  String description;
  double unitPrice;

  Map<String,dynamic> toMap(){
    var map= <String,dynamic>{  
    DbHelper.COLUMN_NAME : name,
    DbHelper.COLUMN_DESCRIPTION : description,
    DbHelper.COLUMN_UNITPRICE : unitPrice};
    if(id!=null) {
      map[DbHelper.COLUMN_ID] = id;
    }
    return map;
  }
  Product.fromMap(Map<String, dynamic> map) {
    id = map[DbHelper.COLUMN_ID];
    name = map[DbHelper.COLUMN_NAME];
    description = map[DbHelper.COLUMN_DESCRIPTION];
    unitPrice = map[DbHelper.COLUMN_UNITPRICE];
  }
}

Bu db helper

class DbHelper{

  static const String TABLE_PRODUCT = "products";
  static const String COLUMN_ID = "id";
  static const String COLUMN_NAME = "name";
  static const String COLUMN_DESCRIPTION = "description";
  static const String COLUMN_UNITPRICE = "unitPrice";
  
  DbHelper._();
  static final DbHelper dbHelper = DbHelper._();

  
  Database _db;



  Future<Database> get database async{
      if(_db == null){
        _db = await initializeDb();
        return _db;
      }
      return _db;
  }

  Future<Database> initializeDb() async{

    String dbPath = join(await getDatabasesPath(),'etrade.db');
    var eTradeDb = await openDatabase(dbPath,version: 1,onCreate: createDb);

    return eTradeDb;
  }

  void createDb(Database db, int version) async{
    await db.execute('CREATE TABLE $TABLE_PRODUCT($COLUMN_ID INTEGER PRIMARY KEY AUTOINCREMENT,$COLUMN_NAME TEXT,$COLUMN_DESCRIPTION TEXT,$COLUMN_UNITPRICE INTEGER)');
  }

  Future<List<Product>> getProducts() async{

    final db = await database;
    var products = await db.query(TABLE_PRODUCT,columns: [COLUMN_ID,COLUMN_NAME,COLUMN_DESCRIPTION,COLUMN_UNITPRICE]);

    List<Product> productList= List<Product>();  

    products.forEach((currentProduct){
      Product product = Product.fromMap(currentProduct);
      productList.add(product);
    });

    return productList;
 
  }

  Future<int> insert(Product product) async{

    Database db = await database;
    var result = await db.insert("products", product.toMap());
    return result;
  }
  Future<int> delete(int id) async{

    Database db = await database;
    var result = await db.rawDelete("DELETE FROM products WHERE id = $id");
    return result;
  }


   Future<int> update(Product product) async {
    final db = await database;

    return await db.update(
      TABLE_PRODUCT,
      product.toMap(),
      where: "$COLUMN_ID = ?",
      whereArgs: [product.id],
    );
  }


}

Son zamanlarda flutter çıldırdı artık yeşil tik alamıyorum bile kod yazarken hep bir yerlerde uyarı veriyo. Her neyse hem internetten baktığım hemde sizin koddan baktığım kadarıyla böyle bir db_helper ve product class oluşturdum. Ama product.fromMap() kısmı tamamen hata veriyor. db_helper kısmında ise update metodunda whereArgs: [product.id] hata veriyor. Netten de baktım benim yaptığımın aynısı ama benimki hatalı sorun nedir acaba bir bilginiz var mı ?

    @HseyinAkkaya
    Hocam şu awaitler hata veriyor. await sadece async fonksiyonlarla kullanılabiliyor diyor ama fonksiyonlar async zaten.
    List<Food> foods = await db.allFoods();
    Food aFood = await db.getFood(1);
    Food aFood2 = await db.getFoodbyName(“Mercimek Çorbası”);

    Abd await kullandığınız metot da async olmalı. nerede kullandığınızı da atar mısınız

    • Abd likes this.
    • Abd replied to this.

      Abd initstate async olamaz. Bunu yerine async metot oluşturup initstate içinde await olmadan bu metodu çağırın. Scaffold içinde gelen data== null ise progressbar gösterin else kısmında da ekranınızı tasarlayın.

      @HseyinAkkaya
      Hocam tekrar rahatsız ediyorum. 🙂
      Telefon ekranında type Future<dynamic> is not a subtype of type List<Food> yazıyor.
      Konsolda mainde ki MaterialApp için hataya neden oldu tarzı bir şey yazıyor.

      main dosyam
      void main() {
      runApp(MyApp());
      }
      class MyAppextends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      return MaterialApp(initialRoute: '/', routes: {
      '/': (context) => Home(),
      ...

      food ve database kodları tamamen aynı.
      bu da database’i kullandığım sayfa.

      `FoodDatabase db = FoodDatabase();
      Food iFood =Food(name: “Salata”,recipe: “Doğra karıştır”,ingredients: “Domates biber soğan”);

      void ekle() async {
      Food aFood2 = await db.getFoodbyName(“Mercimek Çorbası”);
      aFood2.name=“Lahmacun”;
      db.foodUpdate(aFood2);
      }

      allFoods() async {
      return await db.allFoods();
      }

      @override
      void initState() {
      super.initState();
      db.insertFood(iFood);
      List<Food> foods = allFoods();
      print(foods[0]);
      ekle();
      foods = allFoods();
      print(foods[1]);
      …`

      İlk kod düzgün ikincisi niye tırnak içinde olmasına rağmen düzelmedi anlamadım valla 🙂

      Abd allFoods(); async metod. başına await yazarak kullanmalısınız. ayrıca initstate içinde await kullanamazsınız. await kullanmadığınız için liste değil future dönüyor ve future nesneyi list içine atmaya çalışıyorsunuz. await kullanın ama bunu initstate dışında yapın.

      • Abd replied to this.

        HseyinAkkaya
        FoodDatabase db = FoodDatabase();
        Food iFood =Food(name: “Salata”,recipe: “Doğra karıştır”,ingredients: “Domates biber soğan”);
        Bu ilk iki satır initState dışında.
        db.insertFood(iFood);
        Bu satır initState dışında the name of a constructor must match the name of the enclosing class diye hata veriyor.
        initState içinde kullandığımda altında bir hata çıkmıyor. Üstelik async olmasına rağmen awaitsiz kullanıyoruz. awaitle kullanılmıyor zaten. initState içinde ki haliyle kodu çalıştırınca bir yerde hata fırlatıyor ayrıca ama onu tam anlayamadım. 🙂

        Benim amacım uygulama açıldığında databaseye sadece bir tane Food eklemek ve daha sonra bu Food’un değerleriyle oynamak. İkinci bir food eklemeyeceğim. Bunu nasıl yapabilirim? Database’i nerede oluşturup, bu foodu nerede nasıl insert etmem gerekiyor? 🙂

          8 months later

          HseyinAkkaya Hüseyin bey merhaba, hemen hemen aynı yapıya sahip soru cevap uygulamam mevcut. uygulama ilk kurulumda localDB oluşturuyor. kullanıcılar veritabanından gelen soru ve cevaplara göre soru cevaplayıp, doğru yanlış cevapları görebiliyor, sonuç kısmında da cevap anahtarı ekranı görüyorlar. aşağı yukarı uygulama çalışma mantığı bu.

          asıl sormak istediğim ise şu. uygulamamın 500 tane kullanıcısı var. play storedan indirdiler kullanıyorlar. ancak ben ilerleyen süreç ile birlikte uygulamamın veritabanına yeni sorular ekleyip güncelleme vermek istiyorum. ancak sorun şu ki uygulama kurulu olan kullanıcılar güncelleme yapınca yeni eklenen soruları göremiyorlar. uygulama kaldırılıp yeniden kurulursa db olmadığı için yeniden db oluşturuluyor ve tüm soruları görebiliyorlar tabi. ama var olan DB yi güncelleme ile yenisi ile değiştiremiyorum. bu durum ile ilgili bana yardımcı olabilir misiniz? örnek bir kod bloğunuz var mı acaba bu durum ile ilgili. şimdiden teşekkürler.

          Madlove Bunu basit olarak şu şekilde yapabilirsiniz. Veritabanına birde versiyon numarası atın. Normalde sqflite ile veritabanı oluştururken bir versiyon numarası atıyordu ama ne kadar tutarlı çalışır tartışılır. Siz bir tabloda bunu tutabilirsiniz. Veritabanı yoksa zaten oluşturuluyor bunda sıkıntı yok. Ama veritabanı halihazırda varsa bir if sorgusu ile güncel olup olmadığını öğrenebilirsiniz. Program içinde bu versiyonu sorgularsınız. Eğer versiyon numarası daha önceden eklenmemişse veya şuanki versiyondan küçükse sorular tablosunu silip güncel sorularla yeniden oluşturun ve versiyon tablosundaki değeri güncelleyin. Eğer versiyon tablosu yoksa onu da güncel değer ile oluşturun.
          Bu şekilde sorularda değişiklik yaparak yayınladığınız her sürümde db versiyonunu 1 artırarak telefonun bunu algılamasını sağlamış olursunuz. Hatta versiyonu tablo yerine shared_preferences ile de tutabilirsiniz. versiyon null veya şimdikinden küçükse işlem yapabilirsiniz.

          Write a Reply...