ข้ามไปยังเนื้อหาหลัก
คู่มือระบบ เนื้อหาผ่านการตรวจสอบแล้ว

การจัดการ Local Storage ด้วย SharedPreferences และ SQLite

บทความนี้กล่าวถึงวิธีการจัดการข้อมูลในเครื่อง (Local Storage) ใน Flutter โดยเน้นที่สองวิธีหลักคือ SharedPreferences สำหรับข้อมูลขนาดเล็ก เช่น การตั้งค่าผู้ใช้ แ...

การจัดการ Local Storage ด้วย SharedPreferences และ SQLite ใน Flutter

ในบทความที่แล้ว เราได้เรียนรู้วิธีการดึงข้อมูลจาก API เพื่อนำมาแสดงผลในแอปพลิเคชัน Flutter ของเรา แต่ข้อมูลที่ดึงมานั้นเป็นข้อมูลที่อยู่บน Server เมื่อแอปพลิเคชันปิดไป ข้อมูลเหล่านั้นก็จะหายไป ในบทความนี้ เราจะมาเรียนรู้วิธีการจัดการข้อมูลในเครื่อง (Local Storage) เพื่อให้ข้อมูลยังคงอยู่แม้ว่าแอปพลิเคชันจะถูกปิดไปแล้วก็ตาม โดยเราจะครอบคลุมถึง SharedPreferences และ SQLite ซึ่งเป็นสองวิธีที่นิยมใช้กันในการจัดการข้อมูลในเครื่อง

SharedPreferences: สำหรับข้อมูลขนาดเล็ก

SharedPreferences เป็นวิธีที่ง่ายที่สุดในการจัดเก็บข้อมูลขนาดเล็ก เช่น การตั้งค่าของผู้ใช้ (User Preferences), ข้อมูลการ Login หรือข้อมูลอื่นๆ ที่มีขนาดไม่ใหญ่มากนัก SharedPreferences จะจัดเก็บข้อมูลในรูปแบบ key-value pair โดยข้อมูลจะถูกจัดเก็บในรูปแบบ XML (บน Android) หรือ plist (บน iOS)

การใช้งาน SharedPreferences

ในการใช้งาน SharedPreferences เราจะต้องทำการ import package `shared_preferences` เข้ามาในโปรเจกต์ของเราก่อน โดยการเพิ่ม dependency เข้าไปในไฟล์ `pubspec.yaml`:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2 # ตรวจสอบเวอร์ชันล่าสุดเสมอ

หลังจากนั้นให้รันคำสั่ง `flutter pub get` เพื่อทำการติดตั้ง package

การอ่านและเขียนข้อมูลด้วย SharedPreferences

เมื่อติดตั้ง package เรียบร้อยแล้ว เราสามารถใช้งาน SharedPreferences ได้ดังนี้:

import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  // Initialize Flutter binding เพื่อให้แน่ใจว่า SharedPreferences ถูก initialize ก่อน
  WidgetsFlutterBinding.ensureInitialized();

  // ดึง instance ของ SharedPreferences
  final prefs = await SharedPreferences.getInstance();

  // เขียนข้อมูล (String)
  await prefs.setString('username', 'JohnDoe');

  // เขียนข้อมูล (Integer)
  await prefs.setInt('age', 30);

  // เขียนข้อมูล (Boolean)
  await prefs.setBool('isLoggedIn', true);

  // อ่านข้อมูล (String)
  final username = prefs.getString('username');
  print('Username: $username'); // Output: Username: JohnDoe

  // อ่านข้อมูล (Integer)
  final age = prefs.getInt('age');
  print('Age: $age'); // Output: Age: 30

  // อ่านข้อมูล (Boolean)
  final isLoggedIn = prefs.getBool('isLoggedIn');
  print('Is Logged In: $isLoggedIn'); // Output: Is Logged In: true

  // ลบข้อมูล
  await prefs.remove('username');

  // ตรวจสอบว่ามี key นี้หรือไม่
  final containsUsername = prefs.containsKey('username');
  print('Contains Username: $containsUsername'); // Output: Contains Username: false

  // ล้างข้อมูลทั้งหมด
  // await prefs.clear();
}

คำอธิบายโค้ด:

  • `SharedPreferences.getInstance()`: เป็น asynchronous function ที่จะคืนค่า instance ของ SharedPreferences
  • `prefs.setString()`, `prefs.setInt()`, `prefs.setBool()`: เป็น method สำหรับเขียนข้อมูลประเภท String, Integer และ Boolean ตามลำดับ
  • `prefs.getString()`, `prefs.getInt()`, `prefs.getBool()`: เป็น method สำหรับอ่านข้อมูลประเภท String, Integer และ Boolean ตามลำดับ
  • `prefs.remove()`: เป็น method สำหรับลบข้อมูล
  • `prefs.containsKey()`: เป็น method สำหรับตรวจสอบว่ามี key นั้นอยู่ใน SharedPreferences หรือไม่
  • `prefs.clear()`: เป็น method สำหรับล้างข้อมูลทั้งหมดใน SharedPreferences

ข้อควรระวัง: SharedPreferences เหมาะสำหรับข้อมูลขนาดเล็กเท่านั้น หากต้องการจัดเก็บข้อมูลที่มีโครงสร้างที่ซับซ้อน หรือข้อมูลที่มีขนาดใหญ่ ควรใช้ SQLite แทน

SQLite: สำหรับข้อมูลที่มีโครงสร้างและขนาดใหญ่

SQLite เป็น relational database management system (RDBMS) ที่มีขนาดเล็กและทำงานแบบ embedded ซึ่งหมายความว่าฐานข้อมูลจะถูกจัดเก็บอยู่ในไฟล์เดียวภายในแอปพลิเคชันของเรา ทำให้ไม่ต้องพึ่งพา Server ภายนอก SQLite เหมาะสำหรับการจัดเก็บข้อมูลที่มีโครงสร้างที่ซับซ้อน เช่น ข้อมูลผู้ใช้, ข้อมูลสินค้า หรือข้อมูลอื่นๆ ที่ต้องการ query ข้อมูลด้วย SQL

การใช้งาน SQLite

ในการใช้งาน SQLite เราจะต้องทำการ import package `sqflite` และ `path_provider` เข้ามาในโปรเจกต์ของเราก่อน โดยการเพิ่ม dependency เข้าไปในไฟล์ `pubspec.yaml`:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.3.0 # ตรวจสอบเวอร์ชันล่าสุดเสมอ
  path_provider: ^2.1.2 # ตรวจสอบเวอร์ชันล่าสุดเสมอ

หลังจากนั้นให้รันคำสั่ง `flutter pub get` เพื่อทำการติดตั้ง package

การสร้างฐานข้อมูลและตาราง

ก่อนที่เราจะสามารถใช้งาน SQLite ได้ เราจะต้องสร้างฐานข้อมูลและตารางก่อน:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';

class DatabaseHelper {
  static final _databaseName = "MyDatabase.db";
  static final _databaseVersion = 1;
  static final table = 'my_table';
  static final columnId = '_id';
  static final columnName = 'name';
  static final columnAge = 'age';

  // ทำให้เป็น singleton class
  DatabaseHelper._privateConstructor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  // only have a single app-wide reference to the database
  static Database? _database;
  Future<Database> get database async {
    if (_database != null) return _database!;
    // lazily instantiate the db the first time it is accessed
    _database = await _initDatabase();
    return _database!;
  }

  // this opens the database (and creates it if it doesn't exist)
  _initDatabase() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, _databaseName);
    return await openDatabase(path,
        version: _databaseVersion, onCreate: _onCreate);
  }

  // SQL code to create the database table
  Future _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE $table (
        $columnId INTEGER PRIMARY KEY AUTOINCREMENT,
        $columnName TEXT NOT NULL,
        $columnAge INTEGER NOT NULL
      )
      ''');
  }

  // Helper methods

  // Inserts a row in the database where each key in the Map is a column name
  // and the value is the column value. The return value is the id of the
  // inserted row.
  Future<int> insert(Map<String, dynamic> row) async {
    Database db = await instance.database;
    return await db.insert(table, row);
  }

  // All of the rows are returned as a list of maps, where each map is
  // a key-value list of columns.
  Future<List<Map<String, dynamic>>> queryAllRows() async {
    Database db = await instance.database;
    return await db.query(table);
  }

  // All of the methods (insert, query, update, delete) can also be done using
  // raw SQL commands. Raw methods are provided to give you full control.
  Future<int?> queryRowCount() async {
    Database db = await instance.database;
    return Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $table'));
  }

  // We are assuming here that the id column in the map is set. The other
  // column values will be used to update the row.
  Future<int> update(Map<String, dynamic> row) async {
    Database db = await instance.database;
    int id = row[columnId];
    return await db.update(table, row, where: '$columnId = ?', whereArgs: [id]);
  }

  // Deletes the row specified by the id. The number of affected rows is
  // returned. This should be 1 as long as the row exists.
  Future<int> delete(int id) async {
    Database db = await instance.database;
    return await db.delete(table, where: '$columnId = ?', whereArgs: [id]);
  }
}

คำอธิบายโค้ด:

  • `DatabaseHelper`: เป็น class ที่จัดการการเชื่อมต่อกับฐานข้อมูล และมี method สำหรับ CRUD operations
  • `_databaseName`: ชื่อของไฟล์ฐานข้อมูล
  • `_databaseVersion`: เวอร์ชันของฐานข้อมูล
  • `table`: ชื่อของตาราง
  • `columnId`, `columnName`, `columnAge`: ชื่อของ column ในตาราง
  • `_initDatabase()`: สร้างและเปิดฐานข้อมูล
  • `_onCreate()`: สร้างตารางในฐานข้อมูล
  • `insert()`: เพิ่มข้อมูลลงในตาราง
  • `queryAllRows()`: ดึงข้อมูลทั้งหมดจากตาราง
  • `queryRowCount()`: นับจำนวนแถวในตาราง
  • `update()`: แก้ไขข้อมูลในตาราง
  • `delete()`: ลบข้อมูลในตาราง

การใช้งาน DatabaseHelper

เราสามารถใช้งาน `DatabaseHelper` ได้ดังนี้:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final dbHelper = DatabaseHelper.instance;

  // row to insert
  Map<String, dynamic> row = {
    DatabaseHelper.columnName : 'John Doe',
    DatabaseHelper.columnAge  : 30
  };

  final id = await dbHelper.insert(row);
  print('inserted row id: $id');

  // query all rows
  final allRows = await dbHelper.queryAllRows();
  print('query all rows:');
  allRows.forEach(print);

  // query row count
  final count = await dbHelper.queryRowCount();
  print('row count is: $count');

  // update row
  Map<String, dynamic> rowToUpdate = {
    DatabaseHelper.columnId : 1,
    DatabaseHelper.columnName : 'Jane Doe',
    DatabaseHelper.columnAge  : 35
  };
  final rowsAffected = await dbHelper.update(rowToUpdate);
  print('updated $rowsAffected row(s)');

  // query all rows after update
  final allRowsAfterUpdate = await dbHelper.queryAllRows();
  print('query all rows after update:');
  allRowsAfterUpdate.forEach(print);

  // delete row
  final deletedRows = await dbHelper.delete(1);
  print('deleted $deletedRows row(s)');

  // query all rows after delete
  final allRowsAfterDelete = await dbHelper.queryAllRows();
  print('query all rows after delete:');
  allRowsAfterDelete.forEach(print);
}

คำอธิบายโค้ด:

โค้ดนี้แสดงให้เห็นถึงการใช้งาน `DatabaseHelper` ในการเพิ่ม, ดึง, แก้ไข และลบข้อมูลในฐานข้อมูล SQLite

สรุป

ในบทความนี้ เราได้เรียนรู้วิธีการจัดการข้อมูลในเครื่องด้วย SharedPreferences และ SQLite โดย SharedPreferences เหมาะสำหรับข้อมูลขนาดเล็กและการตั้งค่าของผู้ใช้ ในขณะที่ SQLite เหมาะสำหรับข้อมูลที่มีโครงสร้างที่ซับซ้อนและมีขนาดใหญ่ การเลือกใช้วิธีการจัดเก็บข้อมูลที่เหมาะสมจะช่วยให้แอปพลิเคชันของเราทำงานได้อย่างมีประสิทธิภาพและจัดการข้อมูลได้อย่างถูกต้อง

ในบทความถัดไป เราจะเรียนรู้วิธีการใช้ Firebase Authentication ใน Flutter เพื่อจัดการการยืนยันตัวตนของผู้ใช้

Office.in.th Custom Service

ต้องการพัฒนาระบบหรือปรับแต่งจากโครงสร้างนี้?

Office.in.th รับพัฒนาและออกแบบ Enterprise Web Application, API Integration และระบบบริหารงานภาครัฐ/เอกชน พร้อมการสนับสนุนระยะยาว

ดูบริการรับพัฒนาซอฟต์แวร์