Kodokon kodokon.com

การจัดเก็บข้อมูลถาวรในเครื่องด้วย shared_preferences

จัดเก็บการตั้งค่าและข้อมูลขนาดเบาอย่างถาวรด้วย shared_preferences พร้อมรู้ถึงข้อจำกัดด้านความปลอดภัยและปริมาณของมัน

8 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

shared_preferences คือที่เก็บข้อมูลแบบคีย์/ค่า (key/value) ที่อาศัยกลไกเนทีฟ (SharedPreferences บน Android, UserDefaults บน iOS) จุดที่มันเหมาะที่สุดคือ ข้อมูลขนาดเล็ก เช่น ธีมที่เลือก การเห็นหน้าจอแนะนำการใช้งานแล้ว หรือแท็บที่ใช้งานล่าสุด นอกเหนือจากนั้น ให้เปลี่ยนเครื่องมือ ใช้ฐานข้อมูล sqflite หรือ drift สำหรับข้อมูลขนาดใหญ่หรือข้อมูลที่ต้องคิวรี และใช้ flutter_secure_storage สำหรับอะไรก็ตามที่ละเอียดอ่อน ไฟล์นี้ถูกโหลดเข้าหน่วยความจำทั้งหมด มันไม่ได้ถูกเข้ารหัสและไม่ได้ถูกสร้างมาเพื่อให้ขยายใหญ่ขึ้น

BASH
flutter pub add shared_preferences

getInstance() เป็นแบบอะซิงโครนัสในการเรียกครั้งแรก (อ่านไฟล์) จากนั้นจะคืนอินสแตนซ์ที่แคชไว้ การเรียก get* ครั้งต่อ ๆ มาจึงเป็น แบบซิงโครนัส โดยดึงจากหน่วยความจำ ส่วนการเรียก set* นั้นเขียนลงดิสก์แบบอะซิงโครนัส ในทางปฏิบัติ อย่ากระจายสตริงคีย์ไปทั่วทั้งแอปพลิเคชัน จงรวมพวกมันไว้ที่ศูนย์กลางในตัวห่อหุ้ม (wrapper) ที่มีชนิดชัดเจน ฉีดเข้าไปได้ และทดสอบได้

DART
import 'package:shared_preferences/shared_preferences.dart';

class SettingsStore {
  SettingsStore(this._prefs);

  static const _darkModeKey = 'settings.darkMode';

  final SharedPreferences _prefs;

  static Future<SettingsStore> load() async {
    final prefs = await SharedPreferences.getInstance();
    return SettingsStore(prefs);
  }

  bool get darkMode =>
      _prefs.getBool(_darkModeKey) ?? false;

  Future<void> setDarkMode(bool value) =>
      _prefs.setBool(_darkModeKey, value);
}
คีย์ที่รวมศูนย์ ชนิดที่รับประกันได้ และดีเพนเดนซีที่ฉีดเข้าไปได้

API นี้เก็บได้เฉพาะชนิดข้อมูลพื้นฐานเท่านั้น ได้แก่ bool, int, double, String และ List<String> สำหรับอ็อบเจกต์ที่มีโครงสร้าง ให้ทำให้มันเป็นอนุกรมเป็น JSON โดย toJson/fromJson ของคุณจากบทเรียนแรกกลับมามีประโยชน์อีกครั้งตรงนี้ ข้อแลกเปลี่ยนที่ยอมรับได้คือ การบันทึกแต่ละครั้งจะเขียนค่าทั้งหมดทับใหม่ และไม่สามารถคิวรีได้ เหมาะอย่างยิ่งสำหรับฉบับร่าง แต่ไม่เหมาะกับประวัติ

DART
import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

class Draft {
  const Draft({required this.title, required this.body});

  factory Draft.fromJson(Map<String, dynamic> json) =>
      Draft(
        title: json['title'] as String,
        body: json['body'] as String,
      );

  final String title;
  final String body;

  Map<String, dynamic> toJson() =>
      {'title': title, 'body': body};
}

Future<void> saveDraft(Draft draft) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('draft', jsonEncode(draft));
}

Future<Draft?> loadDraft() async {
  final prefs = await SharedPreferences.getInstance();
  final raw = prefs.getString('draft');
  if (raw == null) return null;
  return Draft.fromJson(
    jsonDecode(raw) as Map<String, dynamic>,
  );
}
อ็อบเจกต์ที่สมบูรณ์ถูกเก็บเป็นสตริง JSON ธรรมดา

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ข้อมูลใดที่เหมาะจะอยู่ใน shared_preferences?
    • โทเคนการยืนยันตัวตนของผู้ใช้
    • ประวัติธุรกรรมทั้งหมด 50,000 รายการของผู้ใช้
    • การเลือกใช้ธีมมืด
  2. หลังจาก getInstance() ครั้งแรก การอ่านด้วย get* มีพฤติกรรมอย่างไร?
    • การอ่านแต่ละครั้งจะอ่านไฟล์จากดิสก์ใหม่
    • พวกมันเป็นแบบซิงโครนัส ค่าต่าง ๆ ถูกดึงมาจากแคชในหน่วยความจำ
    • พวกมันคืน Future ที่คุณต้อง await ทุกครั้ง
  3. คุณจะบันทึกอ็อบเจกต์ Draft ที่มีโครงสร้างได้อย่างไร?
    • ทำให้มันเป็นอนุกรมเป็น JSON ด้วย jsonEncode แล้วเก็บมันผ่าน setString
    • ใช้ prefs.setObject('draft', draft) ที่ออกแบบมาเพื่อการนี้
    • แยกมันทีละฟิลด์ลงใน setStringList