Irbisa · Cheatsheet
Local Storage
SharedPreferences, sqflite, Hive, flutter_secure_storage
When would you use
SharedPreferences, sqflite, Hive orflutter_secure_storage?How do you open a sqflite database and do basic CRUD on it?
Future<Database> openAppDb() async => openDatabase( join(await getDatabasesPath(), 'app.db'), // join from package:path version: 1, onCreate: (db, version) => db.execute( 'CREATE TABLE notes(id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0)',…What are the typical pitfalls of SharedPreferences and how do you avoid them?
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); // Load once before the first frame, so the UI never flashes a default theme. final prefs = await SharedPreferencesWithCache.create( cacheOptions: const SharedPreferencesWithCacheOptions( allowList: {'theme', 'filters'},…What is
path_provider, and which directory should a file go to: documents, application support, temporary or cache?Future<void> saveFiles(Note note, List<int> thumbBytes) async { // user content: backed up, the user would miss it final docs = await getApplicationDocumentsDirectory(); await File(p.join(docs.path, '${note.id}.json')) .writeAsString(jsonEncode(note.toJson()));…Your sqflite insert throws as soon as you pass a
DateTime: how should dates, bools and enums be stored and read back?enum Priority { low, normal, high } class Task { Task({this.id, required this.title, required this.done, required this.dueAt, required this.priority}); final int? id;…What is the difference between legacy
SharedPreferences,SharedPreferencesAsyncandSharedPreferencesWithCache?import 'package:shared_preferences/shared_preferences.dart';import 'package:shared_preferences/util/legacy_to_async_migration_util.dart'; Future<SharedPreferencesWithCache> initPrefs() async { const options = SharedPreferencesOptions(); // One-time copy of keys the legacy API wrote (a different backend on Android).…You need to see what your app actually wrote to disk on a test device: where do those files live and how do you open them?
# Android, debuggable build: copy the database together with its WAL fileadb exec-out run-as com.example.app cat databases/app.db > app.dbadb exec-out run-as com.example.app cat databases/app.db-wal > app.db-wal # Preferences written by the legacy SharedPreferences APIadb exec-out run-as com.example.app cat shared_prefs/FlutterSharedPreferences.xml…How do you handle schema migrations in sqflite?
Future<Database> openAppDb() async => openDatabase( join(await getDatabasesPath(), 'app.db'), version: 3, // Fresh install: create the latest schema directly. onCreate: (db, version) => db.execute( 'CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT NOT NULL, '…Release 2 adds a
due_atcolumn. Updated users are fine, but fresh installs crash withno such column: due_at. Why?Future<Database> openDb(String path) => openDatabase( path, version: 2, onCreate: (db, version) async { await db.execute( 'CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT NOT NULL)'); }, onUpgrade: (db, oldVersion, newVersion) async { if (oldVersion < 2) { await db.execute('ALTER TABLE tasks ADD COLUMN due_at INTEGER'); } },);How do Hive type adapters and box encryption work?
@HiveType(typeId: 1)class User extends HiveObject { User({required this.name, required this.email}); @HiveField(0) String name;…How does
flutter_secure_storageactually protect data on iOS and on Android?When would you choose Drift over raw sqflite?
class Tasks extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text().withLength(min: 1, max: 200)(); BoolColumn get done => boolean().withDefault(const Constant(false))(); DateTimeColumn get dueAt => dateTime().nullable()();}…What are Isar and ObjectBox, and when do they beat sqflite?
This sqflite transaction never completes and the log warns that the database has been locked: what is wrong?
Future<void> moveTask(Database db, int taskId, int listId) async { await db.transaction((txn) async { await txn.update('tasks', {'list_id': listId}, where: 'id = ?', whereArgs: [taskId]); final count = Sqflite.firstIntValue(await db.rawQuery( 'SELECT COUNT(*) FROM tasks WHERE list_id = ?', [listId])); await txn.update('lists', {'task_count': count}, where: 'id = ?', whereArgs: [listId]); });}Find the bug: this
NotesDaopasses its unit tests, but on a screen that loads and saves at once some calls fail with aDatabaseException.class NotesDao { NotesDao(this.path); final String path; Future<T> _use<T>(Future<T> Function(Database db) body) async { final db = await openDatabase(path); try { return await body(db); } finally { await db.close(); } } Future<List<Map<String, Object?>>> all() => _use((db) => db.query('notes')); Future<int> add(String text) => _use((db) => db.insert('notes', {'text': text}));}Importing a 20 MB JSON export freezes the UI for four seconds even though every storage call is awaited: where is the time actually going?
// wrong: jsonDecode blocks the UI isolate, plus one hop per rowFuture<void> importSlow(Database db, String json) async { final rows = (jsonDecode(json) as List).cast<Map<String, Object?>>(); for (final row in rows) { await db.insert('items', row); }…Users complain the app is eating gigabytes of storage. How do you find out where it went and keep it bounded?
Future<Map<String, int>> measureStorage() async { final dirs = { 'documents': await getApplicationDocumentsDirectory(), 'support': await getApplicationSupportDirectory(), 'cache': await getApplicationCacheDirectory(), };…A list built from sqflite doesn't refresh when a background sync writes new rows: how do you make local storage reactive without polling?
class TaskRepository { TaskRepository(this._db); final Database _db; final _changes = StreamController<List<Task>>.broadcast(); Stream<List<Task>> watchOpen() async* {…What has to be wiped when a user logs out, and what do teams usually forget?
What goes wrong with concurrent writes to a local database, and how do you avoid it?
// Optimistic: the update lands only if nobody changed the row since we read it.Future<bool> rename(Database db, int id, String title, int readRevision) async { final changed = await db.update( 'tasks', {'title': title, 'revision': readRevision + 1}, where: 'id = ? AND revision = ?',…A sqflite query was instant with 500 rows and takes two seconds at 50 000. How do you diagnose and fix it?
// 1. Ask SQLite what it does with the slow query.final plan = await db.rawQuery( 'EXPLAIN QUERY PLAN SELECT * FROM orders ' 'WHERE user_id = ? ORDER BY created_at DESC LIMIT 20', [userId],);…Users who set up a new phone from a backup say the app opens empty or crashes on the database: what did the restore actually do?
<!-- res/xml/data_extraction_rules.xml (Android 12+), referenced by android:dataExtractionRules in AndroidManifest.xml. Android 11 and lower read android:fullBackupContent instead. --><data-extraction-rules> <cloud-backup> <!-- Encrypted with a Keystore key that never leaves the device -->…The key-value package your app is built on is unmaintained: how do you move a shipped app's local data to a new store without losing any of it?
Your FCM background handler bumps a counter in SharedPreferences and Hive, but the UI only sees the new value after a restart: why?
@pragma('vm:entry-point')Future<void> onBackgroundMessage(RemoteMessage message) async { // wrong: the UI isolate cached its map at launch and never sees this write // final prefs = await SharedPreferences.getInstance(); // right: no Dart-side cache, every call goes to the platform store…Crashlytics shows a steady trickle of "database disk image is malformed": how do you find the cause and get those users working again?