23 Eylül 2022 Cuma

Node.js ile Mysql veritabanı üzerinde CRUD operasyonları gerçekleştirme

 Merhaba arkadaşlar bugün sizlere mysql üzerinde node.js ile beraber rest api oluşturacağız. Aslında crud operasyonlarının hepsini katmanları da dahil ederek güvenli ve kullanımı anlaşılır bir sistem yapmaya çalışacağız. 

    Tabiki bu sistem sql injektion saldırılarına karşı açık olan bir yöntem olabilir. Ama asıl amacımız bizim node.js ile rest api oluşturmak. Sonraki yazımız da Mysql orm için kullanılan Sequelize ile rest api oluşturacağız. Orada  model mimarileri oluşturma ve sql cümleciklerini kullanmadan sorgularımızı çalıştırma yoluna gideceğiz. Lafı çok uzatmadan başlayalım bence..


Evet arkadaşlar öncelikle proje klasörünü oluşturmuş olarak sayıyorum sizi vs code üzerinde klasör konumuna cd tutorial-api diyerek konumlandırma işlemlerini tamamlıyoruz daha sonrasında node paketleri için node init -y diyerek package.json dosyasının oluşmasını sağlıyoruz.


Daha sonrasında sırasıyla ihtiyacımız olacak paketleri npm i ... veya npm install ... paketlerin indirilmesi aşamasına geçmiş bulunuyoruz. Alt kısımda ben kullandığım npm paketlerini paylaşacağım.

"dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.1",
    "mysql": "^2.18.1",
    "nodemon": "^2.0.20"
  }

Evet npm paketlerini de kurduk peki sonrasında ne yapıyoruz. Node js çalışabilmesi için server.js veya app.js isim size kalmış dosyamızı oluşturuyoruz.

Ben server.js diye isimlendirdim ve içerisine express server için kodlarımızı yazmaya başlayalım..

 const express = require("express");


const cors = require("cors");

const app = express();

Öncelikle ana kodlarımız require diyerek onları kullanacağımızı belirtiyoruz. 

Express require çünkü bizim için server tanımlaması yapacak.

Cors require çünkü bizim için cors hatalarına engel olacak ve son olarak da ana uygulamamız app diyerek express üzerinde betimlemesini yapmış olacağız.

// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(
  express.urlencoded({ extended: true })
); /* bodyParser.urlencoded() is deprecated */

Daha sonrasında app üzerinde json parse işlemleri için bu parametreyide geçiyoruz.

Parse işlemleri için  gelen methodlara göre tanılamalarımızı atlamıyoruz. Ana dizini de belirtip projenin yayında olduğunu test etmek için yolunu da belirtiyoruz.

// simple route
app.get("/", (req, res) => {
  res.json({ message: "Welcome to tutorial backend application." });
});

Ve uygulamaya controllers kullanacağımızı route.js den gelen istekleri yönetimini yapacağımız dosyayı yani  tutorial.controlller.js dosyasını da app üzerinde require olarak tanımlayıp projenin listen olmasına geçiyoruz.

require("./app/routes/tutorial.routes.js")(app);

Eğer dışarıdan bir port .env dosyasından girilmediyse sana express sunucusuna buradan yayın aç ve dinle demek için server.js dosyasını son eklememezi yapıyoruz.

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

Ben buraya kadar projeyi oluşturduktan sonra şu tarz hatalarla karşılaştım. 

Öncelikle nodemon .\server.js komutunu çalıştırdığımda powershell üzerinde yetki düzenlemesi istedi. Onu ufak bir stackoverflow araştırması sonrası hallettim Eğer bu şekilde hata ile karşılaşırsanız diye linki aşağıda bırakacağım.

Problem: Getting “cannot be loaded because running scripts is disabled on this system” error!

  • Set the execution Policy with the following command: Set-ExecutionPolicy RemoteSigned


Link: https://www.sharepointdiary.com/2014/03/fix-for-powershell-script-cannot-be-loaded-because-running-scripts-is-disabled-on-this-system.html


Bunu da halletikten sonra dosya konumuna app diye yeni bir klasör dizini oluşturuyorum. Bunun içerisinde katman olması ve kullanılabilirlik açısından kolay olsun diye Sırasıyla

config - controllers - models - routes  diye 4 adet klasörümü oluşturuyorum.  Projem bittiğinde aşağıdaki gibi bir klasör dizinine sahip oluyorum.

 
Jpg dosyalarına takılmayınız github reposunda readme.md dosyasında kullanılmak için projenin sonunda eklenmiştir.

Evet arkadaşlar sırasıyla klasör yapımızı da oluşturduk şimdi sırada  ./config/db.config.js dosyasına connection bilgilerini girelim.

module.exports = {
  host: <YOUR_HOST>,
  user: "root",
  password: <YOUR_PASSWORD>,
  database: <YOUR_DATABASE>,
};

    Connection için kullanılacak bilgileri de girdikten sonra ./models klasörü altında sorgularımızı ve model yapımızı oluşturalım ve db.js diye dosya üzerinden connection bilgilerini app üzerinde yollayalım. 

const mysql = require("mysql");
const dbConfig = require("../config/db.config.js");

var connection = mysql.createConnection({
  host: dbConfig.host,
  user: dbConfig.user,
  password: dbConfig.password,
  database: dbConfig.database,
});

    db.js üzerinde require olan alanları belirterek kodlamaya başlıyoruz. dbConfig üzerinden connection bilgilerini ve mysql üzerinde çalışma yapacağımız için iki alanı belirtiyoruz.

// open the MySQL connection
connection.connect((error) => {
  if (error) throw error;
  console.log("Successfully connected to the database.");
});

module.exports = connection;

Sonrasında connection oluşturduktan sonra connect prosesini başlatıyoruz ve node.js üzerinden bize başarılı bağlantı mesajının gelmesini bekliyoruz.

     Db.js de bitti bundan sonra her model için sorgularımızı ve methodlarımızın çalışacak olan tutorial.model.js sayfasını doldurmaya geldi. Buradakiler kısmen sql bilgisi içerse de basit sorgular olduğu için  ayrıntıya girmeden geçeceğim.

Öncelikle require alanlarımızı geçiyoruz.

const sql = require("./db.js");

Sonrasında Tutorial diye bir referans alınacak model oluşturuyoruz ki her defasında  controller tarafında  uzun uzadıya model ismini yazmak zorunda kalmayalım.

--> Models alanı

Hadi methodlarımızı yazalım ve controllere doğru geçelim.

Öncelikle Create methodumuz yani veri ekleme işine yarayacak kodlarımızı yazalım.

Tutorial.create = (newTutorial, result) => {
  sql.query("INSERT INTO tutorials SET ?", newTutorial, (err, res) => {
    if (err) {
      console.log("error: ", err);
      result(err, null);
      return;
    }

    console.log("created tutorial: ", { id: res.insertId, ...newTutorial });
    result(null, { id: res.insertId, ...newTutorial });
  });
};

Burada ki require alanımızı db olarak da görebilirsiniz aslında kullanılan connection üzerinde execute yapmamızı sağlamaktır.  O yüzden sql cümleciği diye sql yazdık diye takılmayın.

Tutorial.findById = (id, result) => {
  sql.query(`SELECT * FROM tutorials WHERE id= ${id}`, (err, res) => {
    if (err) {
      console.log("error: ", err);
      result(err, null);
      return;
    }

    if (res.length) {
      console.log("found tutorial: ", res[0]);
      result(null, res[0]);
      return;
    }

    // not found Tutorial with the id
    result({ kind: "not_found" }, null);
  });
};

findById methodunu da aktif ettik burada id kısmında  where=${id} string  interpolation kullandığımızı görebilirsiniz. Merak edenler için string interpolation nedir ?

Sonrasında getAll  ve parametreye bağlı getirmeyi de ekledik. Eğer ki get methodu çalışırken title dolu giderse sistem title nesnesi içerisinde ki geçenlere göre arama yapacak şekilde listeleme yapıyor.

Tutorial.getAll = (title, result) => {
    let query = "SELECT * FROM tutorials";
 
    if (title) {
      query += ` WHERE title LIKE '%${title}%'`;
    }
 
    sql.query(query, (err, res) => {
      if (err) {
        console.log("error: ", err);
        result(null, err);
        return;
      }
 
      console.log("tutorials: ", res);
      result(null, res);
    });
  };

Yoksa da zaten bütün sonuçları geri bize dönderiyor. Son iki model methodumuz kaldı onları da ekleyelim route ve controller sürecine geçelim.

  Tutorial.updateById = (id, tutorial, result) => {
    sql.query(
      "UPDATE tutorials SET title = ?, description = ?, published = ? WHERE id = ?",
      [tutorial.title, tutorial.description, tutorial.published, id],
      (err, res) => {
        if (err) {
          console.log("error: ", err);
          result(null, err);
          return;
        }
 
        if (res.affectedRows == 0) {
          // not found Tutorial with the id
          result({ kind: "not_found" }, null);
          return;
        }
 
        console.log("updated tutorial: ", { id: id, ...tutorial });
        result(null, { id: id, ...tutorial });
      }
    );
  };

Update kısmında bütün alanların güncellenme durumuna bağlı olarak bütün alanları ekliyoruz ve parametre olarak gelen bilgiyi array olarak sql query'e ekliyoruz. Tabi bunşarı yaparken iki farklı hata döndürmeyi de unutmuyoruz. Burada ki hatalara bakarsak aranılan id yoktur veya server'a erişilememiştir. 

Bunları rahatlıkla asıl sorguya geçmeden return edebiliriz. Siz daha çok validate durumu sorgulamak isterseniz bunları asıl sorgunuz öncesinde tanımlama ya da middleware paketlerini kurarak mimarinizi daha da sağlamlaştırabilirsiniz.

Son  model methodu olarak da delete operasyonunu gerçekleştirelim.

  Tutorial.remove = (id, result) => {
    sql.query("DELETE FROM tutorials WHERE id = ?", id, (err, res) => {
      if (err) {
        console.log("error: ", err);
        result(null, err);
        return;
      }
 
      if (res.affectedRows == 0) {
        // not found Tutorial with the id
        result({ kind: "not_found" }, null);
        return;
      }
 
      console.log("deleted tutorial with id: ", id);
      result(null, res);
    });
  };

Aslında burada tek method halinde deleteAll ve deleteById şeklinde yapılabilirdi ama kafa karıştırmamak için birbirinden ayırarak süreci tamamlayalım.

 Tutorial.removeAll = result => {
    sql.query("DELETE FROM tutorials", (err, res) => {
      if (err) {
        console.log("error: ", err);
        result(null, err);
        return;
      }
 
      console.log(`deleted ${res.affectedRows} tutorials`);
      result(null, res);
    });
  };


--> Controller alanı

Burada mantık biraz aslında önceki uygulamalara göre karışabilir. Çünkü iç içe durumlar olacak bu durumları tekrar tekrar uygulayarak özümsemekte fayda var bence. Route ve model işlemlerini aslında burada yönetmeye çalışıyoruz. Route gelen bir isteği model'e gitmeden önce doğru yönlenmesini ve doğru sonucun dönmesine ortam sağlıyoruz.

Require alanımızı da geçtikten sonra;

const Tutorial = require("../models/tutorial.model.js");

Save işlemleri için yapılacak model methodunu controller methodu içerisinde belirtelim.

exports.create = (req, res) => {
  //Validate request
  if (!req.body) {
    res.status(400).send({
      message: "Content can not be empty!",
    });
  }

  // Create a Tutorial
  const tutorial = new Tutorial({
    title: req.body.title,
    description: req.body.description,
    published: req.body.published || false,
  });

  console.log(tutorial);

  // Save Tutorial in the database
  Tutorial.create(tutorial, (err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "Some error occurred while creating the Tutorial.",
      });
    else res.send(data);
  });
};

Validate kontrolü yaparak model içerisinde boş bilgi gidip sistem hatasını engellemeye öncelik veriyoruz. Sonrasında dolu olan req.body içerisinde ki gönderilen requestleri yeni bir json nesnesi haline getirip create methodunun içerisine parametre olarak yolluyoruz.

exports.findAll = (req, res) => {
  const title = req.query.title;

  Tutorial.getAll(title, (err, data) => {
    if (err)
      res.status(500).send({
        message: err.message || "some error occured while retrieving tutorials",
      });
    else res.send(data);
  });
};

Hem title parametresine bağlı olarak filtreleme yada bütün sonuçları getirmesi için methodumuz da bu şekilde tanımlıyoruz.

GetById methodumuz da ekleyelim;

// Find a single Tutorial by Id
exports.findOne = (req, res) => {
  Tutorial.findById(req.params.id, (err, data) => {
    if (err) {
      if (err.kind === "not_found") {
        res.status(404).send({
          message: `Not found Tutorial with id ${req.params.id}.`,
        });
      } else {
        res.status(500).send({
          message: "Error retrieving Tutorial with id " + req.params.id,
        });
      }
    } else res.send(data);
  });
};

Arkadaşlar her method içerisinde validate kontrolü yapmayı unutmayın bu tarz önlemler kısmende olsa injection saldırılarına karşı önlem olabilir. Bu tarz durumlar olmasa bile sistemin ekstra hata fırtlatmasını ve servis tarafını kullanan kullanıcıların doğru yol almasını sağlamış olursunuz.

exports.update = (req, res) => {
  //Validate request
  if (!req.body) {
    res.status(400).send({
      message: "Content can not be empty!",
    });
  }

  Tutorial.updateById(
    req.params.id,
    new Tutorial(req.body),
    (err, data) => {
      if (err) {
        if (err.kind === "not_found") {
          res.status(404).send({
            message: `Not found Tutorial with id ${req.params.id}.`
          });
        } else {
          res.status(500).send({
            message: "Error updating Tutorial with id " + req.params.id
          });
        }
      } else res.send(data);
    }
  );

};

Updated methodundan sonrasında delete kısmınıda ekleyelim süreci tamamlayalım.

// Delete a Tutorial with the specified id in the request
exports.delete = (req, res) => {
    Tutorial.remove(req.params.id, (err, data) => {
      if (err) {
        if (err.kind === "not_found") {
          res.status(404).send({
            message: `Not found Tutorial with id ${req.params.id}.`
          });
        } else {
          res.status(500).send({
            message: "Could not delete Tutorial with id " + req.params.id
          });
        }
      } else res.send({ message: `Tutorial was deleted successfully!` });
    });
  };


Arkadaşlar exports sonrası gelen method isimlerine dikkat edelim onlar bizim route işlemlerinde yol almamızı kolaylaştıracaklar. update get ve delete kısımları routing için yönlendirme sağlayacaklar.

Lafı uzatmadan routing kısmına da geçelim ve projenin postman de ki görütünlerini aktaralım sizlere.

Burada ki require alanımız nereden geldiğine dikkat edelim.

const tutorials = require("../controllers/tutorial.controller.js");

Domain yönlendirmesini de son olarak ekleyip routing işlemlerinide tamamlamış oluyoruz.

module.exports = app => {
    const tutorials = require("../controllers/tutorial.controller.js");

    var router = require("express").Router();

    //create a new tutorial
    router.post("/", tutorials.create);

    //retrieve all tutorials
    router.get("/",tutorials.findAll);

    //retrieve get all puslihed tutorials
    router.get("/published", tutorials.findAllPublished);

    //retrieve a single tutorial with id
    router.get("/:id", tutorials.findOne);

    //update tutorial with id
    router.put("/:id", tutorials.update);

    //delete tutorial with id
    router.delete("/:id", tutorials.delete);

    //delete all tutorials
    router.delete("/", tutorials.deleteAll);

    //domain
    app.use('/api/tutorials', router);

};

Projenin Github reposunu da paylaşalım aradaki anlatımdan kaynaklı kopukluklar ortadan kalkmış olsun. Umarım faydalı bir içerik olmuştur. Ben öğrendiklerimi bu şekilde yazarak geliştirme taraftarıyım. Bir çok kaynaktan veriler tüketiyorum onları da kaynak olarak ekleyeceğim. 

Postman Çıktıları github reposundan resimerli paylaşıyorum;

Get Method Postman Result 

Get Method For Title Postman Result

Post Method For Create Operation Postman Result


Faydalı kayanklar;

Github Reposu: https://github.com/learninfinity/CRUD-Operation-using-NodeJS-ExpressJS-MySQL/blob/master/app.js

Github Reposu: https://github.com/CodAffection/Node.js-MySQL-CRUD-Operations

Github Reposu: https://github.com/indraarianggi/nodejs-mysql-api/tree/master/app










8 Şubat 2022 Salı

Flutter Easy Localization Paketi ile Çoklu Dil Desteği Eklenmesi

 Merhaba arkadaşlar todo uygulaması üzerinde çoklu dil desteğini nasıl  yapabiliriz bununla alakalı paketi uygulama da nasıl kullanabiliriz hakkında bir yazı yazalım istedim.


Paketimiz Easy_localization

Link: https://pub.dev/packages/easy_localization

Paket olarak arkadaşlar zaten readme kısımlarını ve installing kısımlarını projeye nasıl entegre ederiz çok güzel açıklamışlar ama oldu ya kısmen bilgi olması açısından bizde dinamik varsayılan alanları nasıl gösterebiliriz bunun hakkında kısa bir yazı yazalım.

Arkadaşlar öncelikle ctrl+shift +p diyerek add dependency diyerek easy_localization paketini en son ki versiyonunu indiriyoruz.

veya

pubspec.yaml dosyasında bu paketi installing seçeneklerinde ki gibi ekleyip  paketi download ediyoruz.

dependencies:
  easy_localization: <last_version>

Sonra  bizden paket folder(klasör) dizinimize assets diye bir klasör açmamızı ve içerisine translations klasörünün içerisine .json dosyalarını eklemimizi istiyor.

  /asserts/translations / => en-US.json veya tr-TR.json

assets
└── translations
    ├── {languageCode}.{ext}                  //only language code
    └── {languageCode}-{countryCode}.{ext}    //or full locale code

Aynı paket ios tarafında localization çalıştırabilmek için  ios/Runner/Info.plist paketi içerisine seçili  kod satırlarını ekleyip kaydetmemizi istiyor.

<key>CFBundleLocalizations</key>
<array>
	<string>en</string>
	<string>nb</string>
</array>

Easy_localization paket olarak main methodunu başlangıç olarak  beni Initialize et diyor. Main kodları arasına bu kodlarımızı da ekliyoruz sonrasında MaterialApp Localization nesnesi ile sarmalıyoruz.

 WidgetsFlutterBinding.ensureInitialized();
  await EasyLocalization.ensureInitialized();

MaterialApp sarmalamak içinse kodlarımız aynı şekilde document sayfasında mevcut bunlar;

 return MaterialApp(
      localizationsDelegates: context.localizationDelegates,
      supportedLocales: context.supportedLocales,
      locale: context.locale,
      home: MyHomePage()
    );

Paketimizi ekledik Material App sarmaladık başlangıçta iniatlize de ettik peki sonrasında ne yapıyoruz. Bu sefer Translations klasörü içerisinde bulunan  en-US.json ve tr-Tr.json dosyalarımızın içerisine json formatında statik verilerimizi giriyoruz.



Json dosyalarımızın içerisini de statik olarak doldurduktan sonra MaterialUI içerisinde .tr() ekleyerek local cihaz bilgisine göre güncelleme işlemini tamamlamış oluyoruz.

Mesela TextField için hintText alanını nasıl local dil bilgisini ekledik.

 TextField(
              autofocus: true,
              style: TextStyle(fontSize: 18),
              decoration: InputDecoration(hintText: 'add_Task'.tr()),
              onSubmitted: (value) {}

Burada text alanı dışında tanımlanan .tr() etiketi Türkçe'den gelen  tr olmuyor buna dikkat edelim. Ayrıca tr-Tr ve en-US dosyalarında ki başlık alanları aynı isimde olması gerektiğini unutmayalım !!

Ayrıca biz  todo uygulamasında hazır paketlerden datetime_picker paketininde local dilini değiştirmek içinde paket içerisinde bulunan locale: bilgisine yeni method tanımlayarak cihaz bilgisini alarak paketi de istenilen dile ulaşmasını sağlayalım.

helper diye ana dizine yenir bir klasör tanımlıyoruz. Klasör altına translation_helper.dart class içerisine local cihaz dil bilgisini alacak bir method oluşturup bunu return eden kodları aşağıya bırakıyorum.

class TranslationHelper {
  TranslationHelper._();

  static getDeviceLanguageBul(BuildContext context) {
    var deviceLanguage = context.deviceLocale.countryCode;
    switch (deviceLanguage) {
      case 'tr':
        return LocaleType.tr;
      case 'en':
        return LocaleType.en;
    }
  }
}

Context nesnesi olmak zorunda olduğunu gözden kaçırmayalım. Çünkü context sayesinde local cihaz bilgisini alıyoruz.

  var deviceLanguage = context.deviceLocale.countryCode;

Bu sayede countryCode bilgisini de return etmiş hazır paket için local dil bilgisini almış bulunmaktayız.

locale: TranslationHelper.getDeviceLanguageBul(context),

Kod bloğunu da context içerisine entegre etmiş bulunmaktayız.

Dil paketini Türkçe seçtikten sonraki ekran görüntüleri;


Dil paketini Ingilizce seçtikten sonraki ekran görüntüleri;



Evet arkadaşlar Easy_localization paketini kullanarak çoklu dil desteğini statik nesneler üzerinde gerçekleştirmiş olduk. Tabi bunu paket üzerinde ki local bilgisine dayalı olarak  güncellemeler yaparak projeyi daha da büyütme şansınız var ama temelinde böyle bir şey olduğunu anlatmak istedim.

Umarım faydalı bir içerik olmuştur. Keyifli okumalar ve iyi kodlamalar arkadaşlar.

Sağlıcakla kalın..


7 Şubat 2022 Pazartesi

Flutter Todo Uygulaması üzerinde Search özelliği oluşturmak

 Merhabalar arkadaşlar;

 Flutter serisine devam ediyoruz. Önceki yazımızda sizlere Flutter Hive Depolama yöntemi anlatmıştık.

 Aynı proje üzerinde bu sefer liste içerisinde search(Arama) özelliği ile belirtilen görev(task) var mı yok mu kontrolü sağlamak ve geçerli görev(task) üzerinde normal crud işlemlerini nasıl yaparız ona değineceğiz. 


Return ettiğimiz Scaffold nesnesi  içerisinde appbar: Appbar içerisinde liste olarak [] iconButton nesneleri gönderiyoruz. Iconbutton üzerinde wrap widgets diyerek gestureDetector ile onTab özelliğini aktif ediyoruz. Yani kısaca kod ekranını paylaşırsak;

appBar: AppBar(
        title: GestureDetector(
          onTap: () {
            _showADdTaskBottomSheet(context);
          },
          child: const Text(
            'Genmot Görev Takip Uygulaması',
            style: TextStyle(color: Colors.black),
          ),
        ),
        centerTitle: false,
        actions: [
          IconButton(
            onPressed: () {
              _showSearchPage();
            },
            icon: Icon(Icons.search),
          ),
          IconButton(
            onPressed: () {
              _showADdTaskBottomSheet(context);
            },
            icon: Icon(Icons.add),
          ),
        ],
      ),

IconButton'un onpressed  özelliğinin içerisine _showSearchPage diye bir local method tanımlıyoruz. Bu method sayesinde bize flutter tarafından showSearch hazır methodunu kullanıma sade bir kod bloğu sağlamış oluyoruz.

 _showSearchPage() async {
    await showSearch(
        context: context, delegate: CustomSearchDelegate(allTask: _allTask));
    _getAllTaskDb();
  }

Bu kod bloğunda  showSearch ile CustomSearchDelegate methoduna çağrışım da bulunuyoruz. Bu asenkron çalışması gereken bir method olduğu için async ve await eklemeyi unutmuyoruz arkadaşlar. Widgets klasörümüze custom_delegate.dart diye yeni bir class oluşturuyoruz ve bu class nesnesini SearchDelegate nesnesinden extends ediyoruz.

class CustomSearchDelegate extends SearchDelegate {}


Klasör ağaç yapımız da bu şekilde olduktan sonra CustomSearchDelegate class extends sonrası build edilmesi gereken 4 method ile beraber geliyor arkadaşlar bunları da doldurtuktan sonra güzel bir search butonuna sahip olacağız.

Peki 4 adet build edilecek olan methodlar neler onları kısaca açıklayalım.

1. Build Actions;

  @override
  List<Widget>? buildActions(BuildContext context) {
    // TODO: implement buildActions
    //Arama kısmında ki sağ taraftaki iconları kontrol etmek için kullanılıyor.
    return [
      IconButton(
          onPressed: () {
            query.isEmpty ? null : query = '';
          },
          icon: Icon(Icons.clear))
    ];
  }

Burası arkadaşlar arama kısmında görünecek butonların  veya iconların olduğu alandır.

2. Build Leading;

  @override
  Widget? buildLeading(BuildContext context) {
    // TODO: implement buildLeading
    // en baştaki iconları
    return GestureDetector(
      onTap: () {
        close(context, null);
      },
      child: Icon(
        Icons.arrow_back_ios,
        color: Colors.black,
        size: 24,
      ),
    );
  }

Burası da icon veya button olarak geri işlemleri için uygulama yapılacak alanı işaret ediyor. Kod bloğunda da yaptığımız gibi arrow_back_ios diyerek geri button işareti sağlamış oluyoruz. Material düzeninize bağlı olarak icon renklendirmesi de yapabilirsiniz. O sizin tasarım zevkinize bağlıdır.

3. Build Result

@override
  Widget buildResults(BuildContext context) {}

Arama sonucunda gösterim yapılacak methodları buraya entegre ediyoruz. Arama yapıldıktan sonra geriye dönecek widgets burada return ederek material tasarımımızı tamamlamış oluyoruz.

4. Build Suggestions

 @override
  Widget buildSuggestions(BuildContext context) {}

Arama konusunda daha hassas bir yapı oluşturmak için bu alanda harf duyarlılığı üzerinde arama sonuçlarını göstermek istersek bu alanda return ettiğimiz yapıları buraya entegre ediyoruz.

Buraya kadar ki kısım SearchDelegate bize sağladığı kolaylıkları içeriyordu ve crud işlemleri için Hive Depolama yönteminde yaptığımız kurucu yapıları return ettiğimiz nesneler içerisinde çağırarak sonuca basit şekilde ulaşacağız arkadaşlar.

Delete Operasyonu için;

Öncelikle global olarak CustomSearchDelegate class'ına ana ekranımızdan çekili olan listeyi göndermek için List<allTask> bekleyecek şekilde tanımlama yapıyoruz. Sonrasında bu alanı required olarak tanımlayarak boş geçilme durumunu ortadan kaldırıyoruz.

final List<Task> allTask;

  CustomSearchDelegate({required this.allTask});

home_page.dart => içerisinde çağırırken local method olarak;

 _showSearchPage() async {
    await showSearch(
        context: context, delegate: CustomSearchDelegate(allTask: _allTask));
    _getAllTaskDb();
  }

Required alanları CustomSearchDelegate iletiyoruz.

    List<Task> filteredList = allTask
        .where((task) => task.name.toLowerCase().contains(query.toLowerCase()))
        .toList();

Sonrasında allTask üzerinde where komutuyla aramalarımızı hepsini lowerCase olacak şekilde ayarlayıp sonucu ekrana liste halinde bastırıyoruz.

Gelelim listelenmiş ve arama sonucunda ekrana gelen görev(task) içerisinde delete işlemi için onDismissed () => içerisinde;

 onDismissed: (direction) async {
                    filteredList.removeAt(index);
                    await locator<LocalStorage>()
                        .deleteTask(task: _oAnkiEleman);
                  },

Main methodu içerisinde singelton olarak tanımladığımız locator nesnesini çok uğraşmadan asenkron olarak çağırarak deleteTask(task: _oankiEleman) ile delete işlemi tamamlamış oluyoruz.

Güncelleme işlemleri için;

task_list_item.dart class içerisinde gerçekleştiriyoruz  bütün süreçlerimizi hem widgets üzerinde geziniyoruz ve bu sayede kod karmaşasından ve stabil çalışan bir uygulama yapmış oluyoruz arkadaşlar.

Zaten burası kendi başına bir stateful widget olduğu için kendi içerisinde süreçleri tekrar build ediyor her setState() ettiğimizde searchDelegate için ayrı bir method tanımlaması yapmamıza gerek kalmadan süreci tamamlamış oluyoruz.

   _localStorage.updateTask(task: widget.task);
            setState(() {});




Evet arkadaşlar bir yazımızın daha sonuna geldik. En son olarak bu projeye Easy location ekleyerek dil yapılandırması yapmak istiyorum. Onu da bu yazı içerisine eklemek istemedim kafa karışıklığı ve hizmet ettiği alan sınırlı olsun diye bir sonraki yazıda görüşmek üzere...

İyi kodlamalar...