Flutter Interview Handbook

Local Persistence Engine

100%

Local Persistence Engine

Choosing the correct local storage engine in Flutter impacts app startup latency, memory utilization, transaction isolation, and offline performance. Flutter developers must choose between relational databases (sqflite, drift), NoSQL document engines (Hive, Isar), primitive key-value stores (shared_preferences), and hardware-encrypted vaults (flutter_secure_storage).


1. Local Storage Engine Architectural Matrix

EngineStorage TypeEncryptionQuery EnginePerformance (100k reads)Ideal Use Case
shared_preferencesKey-Value XML/Plist❌ PlaintextSimple key lookupSlow for bulk dataUser settings, flags, theme mode
flutter_secure_storageEncrypted Vault✅ Keychain / KeyStoreKey lookupModerateAuth tokens, API secrets, JWTs
HiveNoSQL Key-Value✅ AES-256Key / Index lookupFast (Memory mapped)Fast local caching, offline feeds
IsarNoSQL Object Database✅ AES-256Rich indexing & filterUltra-Fast (Zero-copy C++)High-volume offline object data
sqflite / driftRelational SQLite✅ SQLCipher optionalFull SQL, JOINs, ACIDModerate (C API bridge)Complex relational schemas, transactions

2. Relational vs. NoSQL Engine Mechanics

Relational Engines (sqflite / drift)

Built on SQLite’s C library:

  • ACID Compliance: Atomicity, Consistency, Isolation, Durability.
  • Relational Integrity: Foreign key constraints, complex JOIN queries, aggregate calculations (SUM, GROUP BY).
  • Overhead: Requires mapping rows to Dart objects (fromMap / toMap) and crossing the C-to-Dart FFI boundary.
// Complex relational query using sqflite
Future<List<Order>> getOrdersWithItems(Database db, int userId) async {
  final List<Map<String, dynamic>> maps = await db.rawQuery('''
    SELECT orders.id, orders.total, items.name 
    FROM orders 
    INNER JOIN items ON orders.id = items.order_id 
    WHERE orders.user_id = ?
  ''', [userId]);
  return maps.map((map) => Order.fromMap(map)).toList();
}

NoSQL Binary Engines (Hive / Isar)

Built for direct Dart object persistence:

  • Memory-Mapped Files (mmap): Reads data directly from disk pages into memory without SQL string parsing.
  • Zero-Copy Serialization: Deserializes binary byte buffers into Dart objects in $O(1)$ time.
// TypeAdapter registration in Hive
@HiveType(typeId: 0)
class UserProfile extends HiveObject {
  @HiveField(0) final String id;
  @HiveField(1) final String name;
  UserProfile({required this.id, required this.name});
}

3. Security: Encrypted Storage Vaults

shared_preferences (Unsafe for Secrets)

Stores data in plain text XML files (shared_prefs/*.xml on Android) or plist files (iOS). On rooted/jailbroken devices, any app or ADB shell can inspect plaintext contents!

flutter_secure_storage (Hardware-Enforced)

  • iOS: Uses the iOS Keychain Services API with hardware-enforced Secure Enclave encryption.
  • Android: Uses Android KeyStore combined with EncryptedSharedPreferences (AES-256 GCM encryption).
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final secureStorage = FlutterSecureStorage();

// Write secret key securely
await secureStorage.write(key: 'jwt_token', value: 'secret_jwt_value');

// Read secret key
final jwtToken = await secureStorage.read(key: 'jwt_token');

4. Trade-offs & Production Considerations

  • Memory Consumption of Memory-Mapped Stores: Hive loads box indices directly into RAM. Storing multi-gigabyte binary files inside Hive boxes causes Out-Of-Memory (OOM) crashes. Use Isar or sqflite with lazy paging for large datasets.
  • Schema Migration Discipline: Relational SQLite requires explicit ALTER TABLE DDL migrations across app updates (onUpgrade). NoSQL object stores require non-breaking schema field tags to prevent deserialization crashes.