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.dartclass 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.dartimport '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.dartimport '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