Skip to content

[Flutter] 使用 sqflite 套件操作 SQLite 儲存資料

Last Updated on 2021-10-13 by Clay

在我們使用 Flutter 開發 APP 時,若有大量『儲存』資料的需求,則通常會使用 SQLite 這類嵌入式的資料庫來儲存資料。今天我要紀錄的,便是在 Flutter 中,如何透過 sqflite 套件來進行 SQLite 嵌入式資料庫的建立、讀取、插入(insert)、刪除(delete),更新(update)資料等操作。


事前準備

要使用 sqflite 套件,必須在 pubspec.yaml 檔案中加入以下套件名稱(或許還加上指定版本):

需要使用的套件分別為 sqflite 以及 path。前者是讓我們操作 SQLite 資料庫的套件、後者是讓我們在不同平台上都能準確讀取指定路徑的套件。

寫下之後,可以再透過 IDE 的輔助或是以下指令:

flutter pub get


來取得套件。


使用 sqflite 建立資料庫

在開始之前,我先說明我的範例程式。我的資料庫目標是要儲存『超級英雄』(SuperHero)的資料,為此我需要 3 份不同的檔案:

lib/
├── DB.dart
├── hero.dart
└── main.dart

  • DB.dart: 資料庫的所有操作都寫在這份檔案中
  • hero.dart: 超級英雄模板的 Class 撰寫於此
  • main.dart: 範例用的程式碼,插入蝙蝠俠、超人等資料,並刪除蝙蝠俠的資料


hero.dart

如上所述,這是超級英雄的模板類別程式。

hero.dart
class SuperHero { // Init final int id; final String name; final int age; final String ability; SuperHero({this.id, this.name, this.age, this.ability,}); // toMap() Map<String, dynamic> toMap() { return { "id": id, "name": name, "age": age, "ability": ability, }; } String toString() { return "SuperHero{\n id: $id\n name: $name\n age: $age\n ability: $ability\n}\n\n"; } }


在這份程式碼中,我定義 SuperHero 這一類別。其中包含著:

  • 編號(id)
  • 名字(name)
  • 年齡(age)
  • 能力(ability)

等不同的項目。

除此之外,則依照官方教程重寫了 toString() 函式,這讓我們能選擇這個類別印出後的格式。


DB.dart

這份程式碼就是資料庫的基本操作了。

DB.dart
import 'dart:async'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:flutter_app/hero.dart'; class HeroDB { static Database database; // Initialize database static Future<Database> initDatabase() async { database = await openDatabase( // Ensure the path is correctly for any platform join(await getDatabasesPath(), "hero_database.db"), onCreate: (db, version) { return db.execute( "CREATE TABLE HEROS(" "id INTEGER PRIMARY KEY," "name TEXT," "age INTEGER," "ability TEXT" ")" ); }, // Version version: 1, ); return database; } // Check database connected static Future<Database> getDatabaseConnect() async { if (database != null) { return database; } else { return await initDatabase(); } } // Show all data static Future<List<SuperHero>> showAllData() async { final Database db = await getDatabaseConnect(); final List<Map<String, dynamic>> maps = await db.query("HEROS"); return List.generate(maps.length, (i) { return SuperHero( id: maps[i]["id"], name: maps[i]["name"], age: maps[i]["age"], ability: maps[i]["ability"], ); }); } // Insert static Future<void> insertData(SuperHero hero) async { final Database db = await getDatabaseConnect(); await db.insert( "HEROS", hero.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } // Update static Future<void> updateData(SuperHero hero) async { final db = await getDatabaseConnect(); await db.update( "HEROS", hero.toMap(), where: "id = ?", whereArgs: [hero.id], ); } // Delete static Future<void> deleteData(int id) async { final db = await getDatabaseConnect(); await db.delete( "HEROS", where: "id = ?", whereArgs: [id], ); } }


乍看之下會覺得很長、很多、很麻煩 —— 實際上定睛一看,會發現其實只有 5 個重要的函式:

  • initDatabase() => 後又被 getDatabaseConnect() 包裝起來。就是建立最基本的函式庫的程式
  • showAllData(): 沒有查詢,直接回傳資料庫中的所有資料
  • insertData(): 插入一筆資料
  • updateData(): 修改一筆資料
  • deleteData(): 刪除一筆資料

其實沒那麼複雜,對吧?


main.dart

那麼,接下來就是最後測試的程式了。別忘了把 hero.dart 以及 DB.dart 都匯入。匯入時不論相對路徑還是絕對路徑都是可以的。

比方說我的專案名稱是 flutter_app(這是預設名稱),那麼我想要匯入 DB.dart,則在程式碼開頭寫下:

import 'package:flutter_app/DB.dart';


即可。

那麼,以下是完整的程式碼,可以看到我匯入了蝙蝠俠、超人等資料;緊接著又刪除了蝙蝠俠的資料。然後我總共印出了兩次。

main.dart
import 'package:flutter/widgets.dart'; import 'package:flutter_app/hero.dart'; import 'package:flutter_app/DB.dart'; void main() async { // Avoid errors WidgetsFlutterBinding.ensureInitialized(); // Open the database //final Future<Database> database = HeroDB.getDatabaseConnect(); // Main work // Batman var batman = SuperHero( id: 0, name: "Batman", age: 50, ability: "Rich", ); // Superman var superman = SuperHero( id: 1, name: "Superman", age: 35, ability: "I can fly", ); // Main work await HeroDB.insertData(batman); await HeroDB.insertData(superman); print(await HeroDB.showAllData()); await HeroDB.deleteData(0); print(await HeroDB.showAllData()); }


Output:

I/flutter ( 9807): [SuperHero{
I/flutter ( 9807):   id: 0
I/flutter ( 9807):   name: Batman
I/flutter ( 9807):   age: 50
I/flutter ( 9807):   ability: Rich
I/flutter ( 9807): }
I/flutter ( 9807): 
I/flutter ( 9807): , SuperHero{
I/flutter ( 9807):   id: 1
I/flutter ( 9807):   name: Superman
I/flutter ( 9807):   age: 35
I/flutter ( 9807):   ability: I can fly
I/flutter ( 9807): }
I/flutter ( 9807): 
I/flutter ( 9807): ]
I/flutter ( 9807): [SuperHero{
I/flutter ( 9807):   id: 1
I/flutter ( 9807):   name: Superman
I/flutter ( 9807):   age: 35
I/flutter ( 9807):   ability: I can fly
I/flutter ( 9807): }
I/flutter ( 9807): 
I/flutter ( 9807): ]


於是你會看到,在第一次印出時,蝙蝠俠和超人都在資料庫中;到第二次印出時,被刪除的蝙蝠俠已經不見了,只留下超人的資料。

順帶一提其實我比較喜歡蝙蝠俠,蝙蝠俠突然消失並不是在暗喻什麼,謝謝大家。


References


Read More

Leave a Reply