© Khmer Angkor Academy - sophearithput168

Local Storage

Local Storage

រក្សាទុកទិន្នន័យក្នុងឧបករណ៍ (device)។ Flutter មាន storage solutions ជាច្រើនប្រភេទ សម្រាប់ការប្រើប្រាស់ផ្សេងៗគ្នា។

� Storage Types Theory

Storage Comparison:

Storage Type Data Size Data Type Use Case
SharedPreferences តូច (KB) String, int, bool, double, List<String> Settings, user preferences, flags
SQLite Database មធ្យម-ធំ (MB-GB) Structured relational data Complex data, relationships, queries
File Storage ធំ (MB-GB) Images, videos, documents, JSON Media files, large documents
Secure Storage តូច (KB) Encrypted strings Passwords, tokens, sensitive data

When to Use Which Storage?

Storage Decision Tree

❓ តើទិន្នន័យជាអ្វី?

├─ 🔐 Sensitive data (password, token)
│  └─ ✅ Use: flutter_secure_storage
│
├─ ⚙️ Settings/Preferences (theme, language)
│  └─ ✅ Use: shared_preferences
│
├─ 📊 Complex structured data (users, products)
│  └─ ✅ Use: SQLite (sqflite)
│
├─ 🖼️ Files (images, videos, PDFs)
│  └─ ✅ Use: File Storage (path_provider)
│
└─ ☁️ Sync across devices
   └─ ✅ Use: Firebase/Cloud storage

📦 SharedPreferences (Key-Value Storage)

Best for: User settings, simple flags, preferences

# pubspec.yaml
dependencies:
  shared_preferences: ^2.2.0
import 'package:shared_preferences/shared_preferences.dart';

// រក្សាទុក
Future<void> saveData() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('name', 'សុខា');
  await prefs.setInt('age', 20);
  await prefs.setBool('isLogin', true);
}

// អាន
Future<void> loadData() async {
  final prefs = await SharedPreferences.getInstance();
  String? name = prefs.getString('name');
  int? age = prefs.getInt('age');
  bool? isLogin = prefs.getBool('isLogin');
}

Complete SharedPreferences Example:

class PreferencesService {
  static const String KEY_THEME = 'theme_mode';
  static const String KEY_LANGUAGE = 'language';
  static const String KEY_USER_ID = 'user_id';
  
  // Save settings
  Future<void> saveSettings({
    required String theme,
    required String language,
    required int userId,
  }) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(KEY_THEME, theme);
    await prefs.setString(KEY_LANGUAGE, language);
    await prefs.setInt(KEY_USER_ID, userId);
  }
  
  // Load settings
  Future<Map<String, dynamic>> loadSettings() async {
    final prefs = await SharedPreferences.getInstance();
    return {
      'theme': prefs.getString(KEY_THEME) ?? 'light',
      'language': prefs.getString(KEY_LANGUAGE) ?? 'km',
      'userId': prefs.getInt(KEY_USER_ID) ?? 0,
    };
  }
  
  // Remove specific key
  Future<void> removeKey(String key) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(key);
  }
  
  // Clear all data
  Future<void> clearAll() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.clear();
  }
}

🗄️ SQLite Database (sqflite)

Best for: Complex data with relationships, querying, filtering

# pubspec.yaml
dependencies:
  sqflite: ^2.3.0
  path: ^1.8.3
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._init();
  static Database? _database;
  
  DatabaseHelper._init();
  
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB('notes.db');
    return _database!;
  }
  
  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);
    
    return await openDatabase(
      path,
      version: 1,
      onCreate: _createDB,
    );
  }
  
  Future _createDB(Database db, int version) async {
    await db.execute('''
      CREATE TABLE notes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        content TEXT NOT NULL,
        created_at TEXT NOT NULL
      )
    ''');
  }
  
  // Create (Insert)
  Future<int> insertNote(Map<String, dynamic> note) async {
    final db = await database;
    return await db.insert('notes', note);
  }
  
  // Read (Select)
  Future<List<Map<String, dynamic>>> getAllNotes() async {
    final db = await database;
    return await db.query('notes', orderBy: 'created_at DESC');
  }
  
  Future<Map<String, dynamic>?> getNoteById(int id) async {
    final db = await database;
    final results = await db.query(
      'notes',
      where: 'id = ?',
      whereArgs: [id],
    );
    return results.isNotEmpty ? results.first : null;
  }
  
  // Update
  Future<int> updateNote(int id, Map<String, dynamic> note) async {
    final db = await database;
    return await db.update(
      'notes',
      note,
      where: 'id = ?',
      whereArgs: [id],
    );
  }
  
  // Delete
  Future<int> deleteNote(int id) async {
    final db = await database;
    return await db.delete(
      'notes',
      where: 'id = ?',
      whereArgs: [id],
    );
  }
  
  // Close database
  Future close() async {
    final db = await database;
    db.close();
  }
}

📁 File Storage (path_provider)

Best for: Images, videos, large files, JSON files

# pubspec.yaml
dependencies:
  path_provider: ^2.1.0
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'dart:convert';

class FileStorageService {
  // Get application documents directory
  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }
  
  // Save text file
  Future<File> saveTextFile(String filename, String content) async {
    final path = await _localPath;
    final file = File(path + '/' + filename);
    return file.writeAsString(content);
  }
  
  // Read text file
  Future<String> readTextFile(String filename) async {
    try {
      final path = await _localPath;
      final file = File(path + '/' + filename);
      return await file.readAsString();
    } catch (e) {
      return '';
    }
  }
  
  // Save JSON
  Future<File> saveJson(String filename, Map<String, dynamic> data) async {
    final jsonString = jsonEncode(data);
    return saveTextFile(filename, jsonString);
  }
  
  // Read JSON
  Future<Map<String, dynamic>> readJson(String filename) async {
    final jsonString = await readTextFile(filename);
    if (jsonString.isEmpty) return {};
    return jsonDecode(jsonString);
  }
  
  // Save image
  Future<File> saveImage(String filename, List<int> bytes) async {
    final path = await _localPath;
    final file = File(path + '/' + filename);
    return file.writeAsBytes(bytes);
  }
  
  // Delete file
  Future<void> deleteFile(String filename) async {
    final path = await _localPath;
    final file = File(path + '/' + filename);
    if (await file.exists()) {
      await file.delete();
    }
  }
  
  // List all files
  Future<List<String>> listFiles() async {
    final path = await _localPath;
    final directory = Directory(path);
    final files = directory.listSync();
    return files.map((file) => file.path.split('/').last).toList();
  }
}

🔐 Secure Storage (flutter_secure_storage)

Best for: Passwords, API tokens, sensitive user data

# pubspec.yaml
dependencies:
  flutter_secure_storage: ^9.0.0
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class SecureStorageService {
  final _storage = FlutterSecureStorage();
  
  // Save secure data
  Future<void> saveToken(String token) async {
    await _storage.write(key: 'auth_token', value: token);
  }
  
  Future<void> saveCredentials(String email, String password) async {
    await _storage.write(key: 'email', value: email);
    await _storage.write(key: 'password', value: password);
  }
  
  // Read secure data
  Future<String?> getToken() async {
    return await _storage.read(key: 'auth_token');
  }
  
  Future<Map<String, String?>> getCredentials() async {
    final email = await _storage.read(key: 'email');
    final password = await _storage.read(key: 'password');
    return {'email': email, 'password': password};
  }
  
  // Delete secure data
  Future<void> deleteToken() async {
    await _storage.delete(key: 'auth_token');
  }
  
  // Clear all secure data
  Future<void> clearAll() async {
    await _storage.deleteAll();
  }
}

🎯 Real-World Example: User Session Manager

class SessionManager {
  final _prefs = SharedPreferences.getInstance();
  final _secureStorage = FlutterSecureStorage();
  
  // Login - Save session
  Future<void> login({
    required int userId,
    required String name,
    required String email,
    required String token,
  }) async {
    // Save non-sensitive data in SharedPreferences
    final prefs = await _prefs;
    await prefs.setInt('user_id', userId);
    await prefs.setString('user_name', name);
    await prefs.setString('user_email', email);
    await prefs.setBool('is_logged_in', true);
    
    // Save sensitive data in Secure Storage
    await _secureStorage.write(key: 'auth_token', value: token);
  }
  
  // Check if logged in
  Future<bool> isLoggedIn() async {
    final prefs = await _prefs;
    return prefs.getBool('is_logged_in') ?? false;
  }
  
  // Get current user
  Future<Map<String, dynamic>?> getCurrentUser() async {
    final prefs = await _prefs;
    final isLoggedIn = prefs.getBool('is_logged_in') ?? false;
    
    if (!isLoggedIn) return null;
    
    return {
      'userId': prefs.getInt('user_id'),
      'name': prefs.getString('user_name'),
      'email': prefs.getString('user_email'),
      'token': await _secureStorage.read(key: 'auth_token'),
    };
  }
  
  // Logout - Clear session
  Future<void> logout() async {
    final prefs = await _prefs;
    await prefs.clear();
    await _secureStorage.deleteAll();
  }
}

💡 Best Practices:

  • ✅ Use SharedPreferences for simple settings only
  • ✅ Use SQLite for complex structured data
  • ✅ Use Secure Storage for passwords and tokens
  • ✅ Use File Storage for large files and media
  • ✅ Always handle read/write errors with try-catch
  • ✅ Close database connections when done
  • ✅ Never store sensitive data in SharedPreferences

⚠️ Security Warning:

  • ❌ Never store passwords in SharedPreferences (not encrypted)
  • ❌ Don't store large data in SharedPreferences (causes lag)
  • ❌ Don't use SQLite for simple key-value data (overkill)
  • ✅ Always use flutter_secure_storage for sensitive data

💡 ជំនួយ: SharedPreferences សម្រាប់រក្សាទិន្នន័យតូចៗ។ SQLite សម្រាប់ទិន្នន័យស្មុគស្មាញ។ Secure Storage សម្រាប់ទិន្នន័យសម្ងាត់។