database

SKILL.md

Data Persistence (Drift, Preferences, Secure Storage)

When to use

  • Drift: large lists/JSON, offline cache, queryable local data.
  • SharedPreferences: user settings and small flags (non-sensitive).
  • flutter_secure_storage: tokens, credentials, secrets (sensitive).

Steps

1) Drift (SQLite): centralized tables, feature-first DAOs

Tables live in the app database file; DAOs live per feature in data/datasource/.

Table example (caching raw JSON by key):

class CachedOrders extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get data => text()();
  TextColumn get cacheKey => text()();
  DateTimeColumn get cachedAt => dateTime().withDefault(currentDateAndTime)();
}

DAO example (delete old → insert new, batch/transaction):

part 'orders_dao.g.dart';

(tables: [CachedOrders])
class OrdersDao extends DatabaseAccessor<AppDatabase> with _$OrdersDaoMixin {
  OrdersDao(super.attachedDatabase);

  Future<void> cacheOrders({required String cacheKey, required List<String> jsons}) async {
    await batch((batch) {
      batch.deleteWhere(cachedOrders, (t) => t.cacheKey.equals(cacheKey));
      batch.insertAll(
        cachedOrders,
        jsons.map((json) => CachedOrdersCompanion.insert(data: json, cacheKey: cacheKey)).toList(),
      );
    });
  }

  Future<List<String>> readOrders({required String cacheKey}) async {
    final query = select(cachedOrders)..where((t) => t.cacheKey.equals(cacheKey));
    return (await query.get()).map((r) => r.data).toList();
  }
}

Regenerate Drift code using the project’s existing command (typically dart run build_runner build -d).

2) SharedPreferences: typed DAO only (no direct getInstance)

Create a typed DAO in the feature datasource folder:

abstract interface class IOrdersPrefsDao {
  PreferencesEntry<bool> get hasSeenOnboarding;
}

final class OrdersPrefsDao extends TypedPreferencesDao implements IOrdersPrefsDao {
  OrdersPrefsDao({required SharedPreferences sharedPreferences})
      : super(sharedPreferences, name: 'orders'); // => typed_orders

  
  PreferencesEntry<bool> get hasSeenOnboarding => boolEntry('has_seen_onboarding');
}

Use .value and .setValue():

final seen = prefs.hasSeenOnboarding.value ?? false;
await prefs.hasSeenOnboarding.setValue(true);

Never store secrets/tokens/credentials in SharedPreferences.

3) Secure storage: flutter_secure_storage for secrets

Wrap flutter_secure_storage behind a small interface so it can be mocked in tests:

abstract interface class ISecureStorage {
  Future<void> write({required String key, required String value});
  Future<String?> read({required String key});
  Future<void> delete({required String key});
}
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final class FlutterSecureStorageAdapter implements ISecureStorage {
  FlutterSecureStorageAdapter(this._storage);
  final FlutterSecureStorage _storage;

  
  Future<void> write({required String key, required String value}) =>
      _storage.write(key: key, value: value);

  
  Future<String?> read({required String key}) => _storage.read(key: key);

  
  Future<void> delete({required String key}) => _storage.delete(key: key);
}
Weekly Installs
2
GitHub Stars
3
First Seen
Feb 27, 2026
Installed on
opencode2
gemini-cli2
antigravity2
claude-code2
github-copilot2
codex2