Flutter 存取 SQLite 本地資料庫

前言

我是以一個大型專案的架構去寫,所以會比較複雜,如果只是要看速成版,可以直接參考官方文件

Sqflite是什麼?SQLite又是什麼?

  1. SQLite 是一種輕量級的、嵌入式的關聯型資料庫管理系統,適合在移動應用中使用,因為它不需要單獨的服務器進程。iOS 和 Android 都內建了 SQLite 支持。
  2. Sqflite 是一個用於 Flutter 的插件,封裝了對 SQLite 的操作,使得開發者可以在 Flutter 應用中方便地使用 SQLite 資料庫,而無需手動編寫原生代碼來處理資料庫操作。
  3.  iOS 操作系統本身包含了 SQLite 資料庫,因此APP要做到資料存取,就是使用Flutter的Sqflit插件來存取SQLite資料庫的資料
  4. Flutter的Sqflite插件來操作SQLite

以下是操作的筆記

檔案架構

因為我是先用DhiWise產生的資料,所以部分沿用他的架構

我沒有列出所有檔案架構,只是紀錄和資料存取有關的架構

  • lib
    • database
      • database_helper.dart
        • entities
          • xxx_entity.dart
        • services
          • xxx_service.dart
    • presentation
      • models
      • widgets
      • provider

撰寫程式碼

步驟1:安裝插件

除了安裝Sqflite插件還會安裝Path插件

Path 插件是一個處理路徑的 Dart 庫,能支援跨平台的路徑操作

使用Path插件能避免遇到在某些平台存取不到資料庫路徑

  1. 網路搜尋插件名稱找到合適的版本dependencies: sqflite: ^2.3.1+1 path: ^1.9.0
  2. 終端機輸入flutter pub get,安裝依賴

步驟2:創建 database_helper

  • 這個檔案主要功能如下
    1. 建立資料庫的連接database 屬性用於建立並返回資料庫的連接。如果 _database 已經初始化,則直接返回;否則,呼叫 _initDb 方法來初始化。
    2. 初始化資料庫
      1. _initDb 方法用於創建資料庫,並指定數據庫的路徑。
      2. getDatabasesPath() 方法獲取設備上的數據庫儲存路徑。
      3. openDatabase 創建或打開一個名為 me_time_db.db 的資料庫,並執行 CREATE TABLE 語句來建立 tasks 資料表。
    3. 建立資料表
      1. 我的範例是Task,然後我只設計3個欄位:id, name, description
  • 程式碼如下
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  static Database? _database;

  factory DatabaseHelper() {
    return _instance;
  }

  DatabaseHelper._internal();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDb();
    return _database!;
  }

  Future<Database> _initDb() async {
    String path = join(await getDatabasesPath(), 'database_name_db.db');
    print('Database path: $path');
    return await openDatabase(
      path,
      version: 3,
      onCreate: (db, version) async  {
        await db.execute(
            '''
            CREATE TABLE tasks (
              task_id TEXT PRIMARY KEY,
              name TEXT,
              description TEXT,
            '''
        );
      },
    );
  }
}

步驟3:建立存取的Model – Entity

class TaskEntity {
  final String taskId;
  final String? name;
  final String? description;

  TaskEntity({
    required this.taskId,
    required this.name,
    required this.description,
  });

  factory TaskEntity.fromJson(Map<String, dynamic> json) {
    return TaskEntity(
      taskId: json['task_id'],
      name: json['name'],
      description: json['description'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'task_id': taskId,
      'name': name,
      'description': description,
    };
  }
}

步驟4:建立存取資料表資料的方法 – Sevice

import 'package:me_time/database/entities/task_entity.dart';
import 'package:sqflite/sqflite.dart';
import '../database_helper.dart';

class TaskService {
  static final TaskService _instance = TaskService._internal();
  factory TaskService() => _instance;
  TaskService._internal();

  Future<Database> get _database async {
    return await DatabaseHelper().database;
  }

  Future<List<TaskEntity>> getTasks() async {
    final db = await _database;
    final List<Map<String, dynamic>> maps = await db.query('tasks');
    return List.generate(maps.length, (i) {
      return TaskEntity(
        taskId: maps[i]['task_id'],
        name: maps[i]['name'],
        description: maps[i]['description'],
      );
    });
  }
  Future<TaskEntity?> getTaskById(String taskId) async {
    final db = await _database;
    final List<Map<String, dynamic>> maps = await db.query(
      'tasks',
      where: 'task_id = ?',
      whereArgs: [taskId],
    );
    if (maps.isNotEmpty) {
      return TaskEntity.fromJson(maps.first);
    }
    return null;
  }

  Future<int> addTask(TaskEntity task) async {
    final db = await _database;
    return await db.insert(
      'tasks',
      task.toJson(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<void> updateTask(TaskEntity updatedTask) async {
    final db = await _database;
    await db.update(
      'tasks',
      updatedTask.toJson(),
      where: 'task_id = ?',
      whereArgs: [updatedTask.taskId],
    );
  }

  Future<void> deleteTask(String taskId) async {
    final db = await _database;
    await db.delete(
      'tasks',
      where: 'task_id = ?',
      whereArgs: [taskId],
    );
  }

  Future<List<TaskEntity>> getAllTasks() async {
    return await getTasks();
  }
}

如何直接查看到SQLite資料庫

開發ios app時,使用模擬器去做測試,想要查看模擬器中的SQLite資料庫時…

  1. 首先你會需要先取得資料庫的路徑前面在database_helper時有寫一段,這段會print出路徑 Future<Database> _initDb() async { String path = join(await getDatabasesPath(), 'me_time_db.db'); print('Database path: $path');可能會得出這樣的路徑/Users/你的用戶名/Library/Developer/CoreSimulator/Devices/模擬器ID/data/Containers/Data/Application/應用ID/Documents/
  2. 使用 Finder 前往該路徑
    1. 打開 Finder,按 Cmd + Shift + G 打開「前往文件夾」對話框。
    2. 將資料庫路徑粘貼進去並按 Enter。這樣會直接打開該文件夾,你應該能夠看到 database_name_db.db
  3. 使用 SQLite 查看工具
    1. 我是使用Table Plus,這款資料庫管理工具,介面簡潔,功能齊全,免費版就很夠用,而且支援很多資料庫,他的備份機制也很好用
    2. 直接到Table Plus官網下載Download for Mac,下載完就可以直接對著database_name_db.db點兩下開啟
  4. 後面就是工程師在操作資料庫的概念了,我就不贅述