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
如上所述,這是超級英雄的模板類別程式。
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,
};
}
@override
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
這份程式碼就是資料庫的基本操作了。
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';
即可。
那麼,以下是完整的程式碼,可以看到我匯入了蝙蝠俠、超人等資料;緊接著又刪除了蝙蝠俠的資料。然後我總共印出了兩次。
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
- https://pub.dev/packages/sqflite
- https://pub.dev/packages/path
- https://flutter.dev/docs/cookbook/persistence/sqlite
- https://medium.com/flutter-community/using-sqlite-in-flutter-187c1a82e8b