~/icsd.ir — bash
SYSTEM_ONLINE

ذخیره‌سازی محلی

اپلیکیشن‌های موبایل به ذخیره‌سازی محلی نیاز دارند: تنظیمات کاربر، cache، session token، داده‌های آفلاین. در این فصل ۴ روش اصلی را بررسی می‌کنیم.

۸.۱ مقدمه

اپلیکیشن‌های موبایل به ذخیره‌سازی محلی نیاز دارند: تنظیمات کاربر، cache، session token، داده‌های آفلاین. در این فصل ۴ روش اصلی را بررسی می‌کنیم.

۸.۲ انتخاب ابزار مناسب

ابزار کاربرد سرعت پیچیدگی
SharedPreferences تنظیمات ساده key-value سریع کم
flutter_secure_storage token، password متوسط کم
Hive NoSQL سبک، typed خیلی سریع متوسط
sqflite SQL relational متوسط زیاد
Isar NoSQL مدرن، با index خیلی سریع متوسط
Drift SQL با type-safety سریع زیاد

۸.۳ SharedPreferences

برای داده‌های ساده key-value (string، int، bool، double، List<String>).


flutter pub add shared_preferences
    

import "package:shared_preferences/shared_preferences.dart";

class SettingsService {
  static const _keyTheme = "theme_mode";
  static const _keyLanguage = "language";
  static const _keyOnboardingDone = "onboarding_done";
  
  // Save
  Future setTheme(String mode) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_keyTheme, mode);
  }
  
  Future setOnboardingDone(bool done) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_keyOnboardingDone, done);
  }
  
  // Read
  Future getTheme() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(_keyTheme) ?? "system";
  }
  
  Future isOnboardingDone() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_keyOnboardingDone) ?? false;
  }
  
  Future clear() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.clear();
  }
}

// API ها:
// setString, setInt, setDouble, setBool, setStringList
// getString, getInt, getDouble, getBool, getStringList
// remove(key), containsKey(key)
    
مهم: SharedPreferences برای داده‌های حساس (token، password) مناسب نیست! از flutter_secure_storage استفاده کنید.

۸.۴ Flutter Secure Storage

برای داده‌های حساس — در Android از Keystore و در iOS از Keychain استفاده می‌کند.


flutter pub add flutter_secure_storage
    

import "package:flutter_secure_storage/flutter_secure_storage.dart";

class TokenStorage {
  static const _storage = FlutterSecureStorage(
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
  );
  
  static const _keyAccess = "access_token";
  static const _keyRefresh = "refresh_token";
  
  Future saveTokens({required String access, required String refresh}) async {
    await _storage.write(key: _keyAccess, value: access);
    await _storage.write(key: _keyRefresh, value: refresh);
  }
  
  Future getAccessToken() => _storage.read(key: _keyAccess);
  Future getRefreshToken() => _storage.read(key: _keyRefresh);
  
  Future clear() async {
    await _storage.deleteAll();
  }
}
    

۸.۵ Hive — NoSQL سریع

Hive یک NoSQL database سبک، fast و pure Dart است. بهترین گزینه برای cache و آفلاین.


flutter pub add hive hive_flutter
flutter pub add --dev hive_generator build_runner
    

// 1. تعریف Model با adapter
import "package:hive/hive.dart";

part "product.g.dart";

@HiveType(typeId: 0)
class Product extends HiveObject {
  @HiveField(0)
  final int id;
  
  @HiveField(1)
  final String name;
  
  @HiveField(2)
  final double price;
  
  @HiveField(3)
  final DateTime cachedAt;
  
  Product({required this.id, required this.name, required this.price, required this.cachedAt});
}

// تولید adapter:
// flutter pub run build_runner build

// 2. Initialize در main
void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(ProductAdapter());
  
  // Open box
  await Hive.openBox("products");
  
  runApp(const MyApp());
}

// 3. استفاده
class ProductCache {
  static const _boxName = "products";
  
  Box get _box => Hive.box(_boxName);
  
  Future save(Product product) async {
    await _box.put(product.id, product);
  }
  
  Future saveAll(List products) async {
    final map = {for (var p in products) p.id: p};
    await _box.putAll(map);
  }
  
  Product? get(int id) => _box.get(id);
  
  List getAll() => _box.values.toList();
  
  Future delete(int id) async => await _box.delete(id);
  
  Future clear() async => await _box.clear();
  
  // Watch تغییرات (مثل Stream)
  Stream watch() => _box.watch();
}

// 4. در Riverpod
@riverpod
class CachedProducts extends _$CachedProducts {
  @override
  List build() {
    final cache = ProductCache();
    
    // listen to changes
    cache.watch().listen((event) {
      state = cache.getAll();
    });
    
    return cache.getAll();
  }
}
    

۸.۶ sqflite — SQL Database

برای داده‌های relational پیچیده با join و query قدرتمند.


flutter pub add sqflite path
    

import "package:sqflite/sqflite.dart";
import "package:path/path.dart";

class DatabaseHelper {
  static Database? _db;
  
  Future get database async {
    if (_db != null) return _db!;
    _db = await _init();
    return _db!;
  }
  
  Future _init() async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, "shop.db");
    
    return openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute("""
          CREATE TABLE products (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            price REAL NOT NULL,
            stock INTEGER DEFAULT 0,
            created_at TEXT
          )
        """);
        
        await db.execute("""
          CREATE TABLE orders (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            total REAL NOT NULL,
            status TEXT DEFAULT "pending",
            created_at TEXT
          )
        """);
        
        await db.execute("CREATE INDEX idx_user ON orders(user_id)");
      },
      onUpgrade: (db, oldVersion, newVersion) async {
        // migration ها
        if (oldVersion < 2) {
          await db.execute("ALTER TABLE products ADD COLUMN description TEXT");
        }
      },
    );
  }
  
  // Insert
  Future insertProduct(Map product) async {
    final db = await database;
    return await db.insert("products", product);
  }
  
  // Bulk insert
  Future insertProducts(List> products) async {
    final db = await database;
    final batch = db.batch();
    for (final p in products) {
      batch.insert("products", p);
    }
    await batch.commit(noResult: true);
  }
  
  // Query
  Future>> getProducts() async {
    final db = await database;
    return await db.query("products", orderBy: "created_at DESC");
  }
  
  // Query پیشرفته
  Future>> searchProducts(String keyword) async {
    final db = await database;
    return await db.query(
      "products",
      where: "name LIKE ?",
      whereArgs: ["%$keyword%"],
      limit: 50,
    );
  }
  
  // Raw SQL
  Future>> getOrdersWithProducts() async {
    final db = await database;
    return await db.rawQuery("""
      SELECT o.*, p.name as product_name 
      FROM orders o
      JOIN order_items oi ON oi.order_id = o.id
      JOIN products p ON p.id = oi.product_id
      WHERE o.user_id = ?
    """, [userId]);
  }
  
  // Update
  Future updateProduct(int id, Map data) async {
    final db = await database;
    return await db.update("products", data, where: "id = ?", whereArgs: [id]);
  }
  
  // Delete
  Future deleteProduct(int id) async {
    final db = await database;
    return await db.delete("products", where: "id = ?", whereArgs: [id]);
  }
  
  // Transaction
  Future placeOrder(Order order, List items) async {
    final db = await database;
    await db.transaction((txn) async {
      final orderId = await txn.insert("orders", order.toMap());
      for (final item in items) {
        await txn.insert("order_items", {...item.toMap(), "order_id": orderId});
        await txn.rawUpdate(
          "UPDATE products SET stock = stock - ? WHERE id = ?",
          [item.qty, item.productId],
        );
      }
    });
  }
}
    

۸.۷ Isar — مدرن‌ترین NoSQL


flutter pub add isar isar_flutter_libs
flutter pub add --dev isar_generator build_runner
    

import "package:isar/isar.dart";

part "product.g.dart";

@collection
class Product {
  Id id = Isar.autoIncrement;
  
  @Index(type: IndexType.value)
  late String name;
  
  late double price;
  
  @Index()
  late DateTime createdAt;
  
  // Relationship
  final category = IsarLink();
}

// استفاده
final isar = await Isar.open([ProductSchema, CategorySchema]);

// Insert
await isar.writeTxn(() async {
  await isar.products.put(Product()..name = "لپ‌تاپ"..price = 50000000);
});

// Query قوی با type-safety
final results = await isar.products
    .filter()
    .priceGreaterThan(1000000)
    .nameContains("laptop", caseSensitive: false)
    .sortByCreatedAtDesc()
    .limit(20)
    .findAll();

// Watch (real-time)
final stream = isar.products.where().watch(fireImmediately: true);
stream.listen((products) => print(products));
    

۸.۸ ذخیره فایل


flutter pub add path_provider
    

import "package:path_provider/path_provider.dart";
import "dart:io";

// Documents directory (پایدار، backup می‌شود)
final dir = await getApplicationDocumentsDirectory();

// Cache directory (می‌تواند پاک شود)
final cacheDir = await getTemporaryDirectory();

// External storage (Android)
final extDir = await getExternalStorageDirectory();

// نوشتن فایل
final file = File("${dir.path}/notes.txt");
await file.writeAsString("متن من");

// خواندن
final content = await file.readAsString();

// نوشتن JSON
import "dart:convert";
await file.writeAsString(jsonEncode({"key": "value"}));

// نوشتن binary
await file.writeAsBytes([1, 2, 3]);

// لیست فایل‌ها
final files = dir.listSync();
for (final f in files) {
  print(f.path);
}
    

۸.۹ بهترین تجربیات

  1. SharedPreferences برای تنظیمات. زیر 1MB.
  2. Secure Storage برای حساس. token، password.
  3. Hive یا Isar برای cache. سریع و typed.
  4. sqflite برای relational. با joinها.
  5. Repository Pattern. abstraction روی storage.
  6. Migration در DB. از روز اول plan کنید.
  7. Encryption برای داده مهم.
  8. Cache invalidation strategy. TTL، manual، event-based.
  9. backup/restore برای پایداری.

۸.۱۰ خلاصه فصل

  • SharedPreferences برای داده‌های ساده
  • flutter_secure_storage برای token
  • Hive برای NoSQL سبک و سریع
  • sqflite برای SQL پیچیده
  • Isar مدرن‌تر با indexing
  • path_provider برای فایل‌ها
در فصل بعد: Form‌ها و Validation.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.