ذخیرهسازی محلی
اپلیکیشنهای موبایل به ذخیرهسازی محلی نیاز دارند: تنظیمات کاربر، 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
۸.۷ 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);
}
۸.۹ بهترین تجربیات
- SharedPreferences برای تنظیمات. زیر 1MB.
- Secure Storage برای حساس. token، password.
- Hive یا Isar برای cache. سریع و typed.
- sqflite برای relational. با joinها.
- Repository Pattern. abstraction روی storage.
- Migration در DB. از روز اول plan کنید.
- Encryption برای داده مهم.
- Cache invalidation strategy. TTL، manual، event-based.
- backup/restore برای پایداری.
۸.۱۰ خلاصه فصل
- SharedPreferences برای دادههای ساده
- flutter_secure_storage برای token
- Hive برای NoSQL سبک و سریع
- sqflite برای SQL پیچیده
- Isar مدرنتر با indexing
- path_provider برای فایلها
در فصل بعد: Formها و Validation.