datalocal_sqlite

SQLite storage adapter for DataLocal. It keeps the DataLocal document API while replacing the default SharedPreferences storage with SQLite and native atomic batch transactions.

Installing this package does not automatically change DataLocal's backend. Select SQLite explicitly by passing DataLocalSqliteStorage() when opening the database.

Install

dependencies:
  datalocal: ^2.0.0
  datalocal_sqlite: ^0.1.0

Then fetch the packages:

flutter pub get

Open a database

import 'package:datalocal/datalocal.dart';
import 'package:datalocal_sqlite/datalocal_sqlite.dart';

final database = await DataLocalDatabase.open(
  name: 'my_app',
  storage: DataLocalSqliteStorage(),
);

final notes = database.mapCollection('notes');

await notes.insert({
  'title': 'Stored in SQLite',
  'completed': false,
});

final snapshot = await notes
    .query()
    .where('completed', isEqualTo: false)
    .orderBy('title')
    .get();

await database.close();

The collection, CRUD, query, watch, typed-document, and batch APIs come from DataLocal. This adapter only supplies the persistence backend.

Encrypted SQLite database

SQLite does not encrypt its database file by itself. To encrypt each stored DataLocal document with AES-256-GCM and keep its key in platform secure storage, configure the encryption provider when opening the database:

const databaseName = 'my_app';

final database = await DataLocalDatabase.open(
  name: databaseName,
  storage: DataLocalSqliteStorage(),
  encryption: DataLocalAesGcmEncryptionProvider(
    keyProvider: DataLocalSecureStorageKeyProvider(
      databaseName: databaseName,
    ),
  ),
);

This protects stored document payloads, not the entire SQLite file or its structural metadata. It also cannot protect plaintext while the application is running on a fully compromised device.

Atomic bulk writes

Use DataLocal's batch API for large inserts. The adapter commits each batch in one native SQLite transaction:

final products = database.mapCollection('products');

await database.writeBatch((batch) {
  for (var index = 0; index < 1000; index++) {
    batch.insert(
      products,
      {
        'name': 'Product $index',
        'category': index % 10,
      },
      id: 'product-$index',
    );
  }
});

For very large imports, several moderate batches are usually friendlier to the UI and memory than creating one enormous batch.

Switching between backends

Both adapters implement the same DataLocalStorage contract:

final storage = useSqlite
    ? DataLocalSqliteStorage()
    : DataLocalSharedPreferencesAsyncStorage();

final database = await DataLocalDatabase.open(
  name: 'my_app',
  storage: storage,
);

Installing both packages does not synchronize or migrate their data. Each backend has its own persisted store. Application code must explicitly perform a migration if it changes an existing database from SharedPreferences to SQLite.

Current capabilities

  • Android, iOS, and macOS through sqflite
  • Atomic SQLite transactions for DataLocal batches
  • WAL journal mode
  • Indexed lookup by collection and document ID
  • DataLocal filters, sorting, cursors, aggregates, and reactive queries
  • Optional DataLocal AES-GCM document encryption

DataLocal queries currently load a collection and evaluate its document fields in Dart. Arbitrary filters and sorting are not yet translated into SQL, so this release does not provide SQL query pushdown or user-defined field indexes. SQLite still improves persistence and bulk-write behavior, but very large collections will need future query-pushdown support for the full benefit of database indexing.

Always close the database when its owner is disposed:

await database.close();

Libraries

datalocal_sqlite
SQLite storage adapter for DataLocal.