iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
Mobile Development

Flutter基礎入門系列 第 14

【Day 14】資料庫與Flutter的結合 - sqflite的實作

  • 分享至 

  • xImage
  •  

前一篇簡單介紹了SQLite與Flutter提供的支援package: sqflite,今天我們就來在我們的應用程式實際操作看看吧!


SQLite的好用網站

推薦給大家一個線上的SQLite編輯器:https://sqliteonline.com/ 可能有些人的電腦與筆者相同,無法直接開啟並閱讀資料庫檔案(.db),或者是不確定自己SQL語法是否正確,想先試跑一次再正式放進程式之中。那麼便可以使用這個線上的SQLite編輯器,除了可以開啟、編輯、儲存資料庫檔案,也可以將檔案視覺化,非常便利!

需要的函式庫 Dependencies

在這部份會用到幾個函式庫,因此記得先加入pubspec.yaml並下載packages,在編寫程式時會一直跳出煩人的錯誤通知。

// pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8
  
  sqflite: ^2.3.3
  path: ^1.9.0       // using join() https://pub.dev/packages/path

Schedrag中 資料庫的Tables

在正式開始我們的Coding之前,先讓筆者我簡單介紹一下這個接下來要做的資料庫是用來存什麼內容的。關於這個程式詳細的說明可參考【Day 09】的文章,而今天要處理的這個資料庫就是用來將使用者建立的行程儲存至裝置中。因此,我們的table最終將長像如此:

id 名稱 類別 花費時間 deadline 備註
1 Ch1 化學 1h00min 2024/10/02 random text 1
2 Ch3 數學 2h00min 2024/10/01 random text 2
3 Ch2 國文 1h30min 2024/10/04 random text 3

程式結構與函式介紹

在這個程式中,有兩個筆者建立的class與本篇主題相關,分別是BlockBlocksDb,接下來就讓我一一介紹他們的程式碼是在做什麼的吧。
因為這次是第一次實作,因此本篇中先暫時不會在資料庫及各個class加入時間與日期的資料型態與其處理方式。

class Block

這個是一個方塊Block,也是應用程式使用者所建立的行程。以下為它的結構:

class Block {
  final String name;
  final String? category, notes;
  int? id;
  Block(
      {required this.name,
      this.id = null,
      this.category = null,
      this.notes = null});

  Map<String, Object?> toMap() {
    return {
      'id': id,
      'name': name,
      'category': category,
      'notes': notes,
    };
  }
}

Block包含了[名稱name], [類別category], [備註notes]和[編號id]。其中,僅有名稱是建立block時必須設定的項目,類別與備註可以留空,而編號則是於加入資料庫時設定。
在sqflite中,要將一筆資料加入資料庫需要先將資料型態轉為Map<String, Object?>,因此Block有著toMap()這個函數來將它的資料全轉換為map。

class BlocksDb

這部份便是本篇的重點,是這個應用程式中專門用於處理blocks與資料庫的class。先來看看它的結構是如何吧。

class BlocksDb {
  String dbFilename, tableName;
  bool dbIsOpen;
  late final db;

  BlocksDb(
      {required this.dbFilename,
      required this.tableName,
      this.dbIsOpen = false});

  Future open() async {...}

  Future<Block> insert(Block aBlock) async {...}
  Future<int> delete(int id) async {...}
  Future<int> update(Block aBlock) async {...}

  Future close() async {...}
}

BlocksDb包含了以下幾個變數:[資料庫檔案名dbFilename], [表格名稱tableName], [資料庫是否已經開啟dbIsOpen], [資料庫db]。考量到未來程式中可能會使用此class來建立複數個資料庫或表格,因此將資料庫檔案明與表格名稱設成變數,可根據應用程式需求做調整。
dbIsOpen是一個用於判斷資料庫檔案是否已經被開啟並可以做編寫的一個布林值。若欲編寫時此變數為false,便會使用open()開啟資料庫檔案,並將值設為true,再繼續原本的工作。而在最後的close(),則會將dbIsOpen設為false,以表示檔案已關閉。

開啟資料庫檔案:openDatabase

這個函式 open()是用於開啟資料庫檔案的,來看一下程式是怎麼做的吧。

  Future open() async {
    db = openDatabase(
      join(await getDatabasesPath(), dbFilename),
      version: 1,
      onCreate: (db, version) async {
        return await db.execute('''CREATE TABLE $tableName (
          id INTEGER PRIMARY KEY,
          name TEXT, category TEXT,
          notes TEXT)''');
      },
    );
    dbIsOpen = true;
  }

openDatabase()是用於開啟資料庫檔案並讓我們之後可以做資料的存取。其中最重要的參數:檔案位置與名稱,是用getDatabasesPath()獲得運作裝置通常資料儲存的資料夾,並用join()來加上檔案名稱。在此筆者將openDatabase回傳的檔案命名為db(database)。

除了設定檔案位置,也可以使用version設定檔案的版本,用於之後的資料庫升級/降級。
除此以外,我們可以利用openDatabase的onCreate來在初次建立資料庫時執行一些內容,在此,我們希望在建立新資料庫時能夠一併建立一個新的表格,而這個我們可以藉由db.execute('SQL Code')來執行SQL的CREATE指令。
若有需求,openDatabase也能夠在檔案開啟時、版本變動時、檔案設定時執行特定指令。詳細說明請點此處

加入新資料:insert

  Future<Block> insert(Block aBlock) async {
    if (!dbIsOpen) open();

    aBlock.id = await db.insert(tableName, aBlock.toMap());
    return aBlock;
  }

首先,在加入新資料前先確認資料庫是否已經開啟。

db.insert可將新的資料插入資料庫db之中。它主要需要兩個參數,表格名稱以及map型態的資料。表格名稱便是在open時所CREATE的表格,而剛剛建立class Block的toMap()則在這裡派上用場了。
db.insert將會回傳資料被存進的列數,也就是編號id。

資料的修改與刪除:delete, update

  Future<int> delete(int id) async {
    if (!dbIsOpen) open();

    return await db.delete(tableName, where: 'id = ?', whereArgs: [id]);
  }

  Future<int> update(Block aBlock) async {
    if (!dbIsOpen) open();
    
    return await db.update(tableName, aBlock.toMap(),
        where: 'id = ?', whereArgs: [aBlock.id]);
  }

delete與update的方式非常接近,update僅僅是多了更新後資料的內容。除了表格名稱,還需要設定一個東西叫做where
where [column_tag] = [column_name]是SQL中的語法,等號左邊是行的名稱,右邊是要在那行尋找的內容。因為id具有獨特性,不會有重複的資料,因此在此選用id來在資料庫中尋找要編輯的資料。

關閉資料庫檔案:close()

  Future close() async {
    dbIsOpen = false;
    return await db.close();
  }

為了保證資料庫編寫完成後能夠正確的關閉,我們需要建立一個close的函式,並將dbIsOpen設為false以防關閉後再次嘗試編寫資料庫而發生錯誤。


實在是非常抱歉,沒能夠如期完成今天這篇文章,明天將會回來再將本文補齊的m(_ _)m

更新:
文章已經補完了,如果先前的版本有造成閱讀上的不便敬請見諒。未來會進可能早點完成文章的,別再拖到最後一刻了(☍﹏⁰。)

如果有任何想說的,歡迎留言及email~
順帶一提,因為本人的新手任務沒有做完,所以暫時無法回覆留言,不過會嘗試盡快完成並回覆所有訊息的!
email: nnyjan02426@gmail.com
Schedrag: nnyjan02426/Schedrag


上一篇
【Day 13】 SQlite: 快速輕便的資料庫
下一篇
【Day 15】來講點不一樣的:SQL Server
系列文
Flutter基礎入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言