SQLite.Encrypted for Flutter

The SQLite.Encrypted Flutter distribution presents the familiar sqflite API backed by SEE on Android, iOS, Windows, macOS, and Linux.

Core rule: Await activateSee() before calling openDatabase() or openReadOnlyDatabase() with key.

Codec support: SQLite.Encrypted currently supports only aes256ofb. Omit the codec or pass null to use aes256ofb; any codec supplied to key, keyBytes, rekey, or rekeyBytes must be aes256ofb. Keyed opens use the same aes256ofb default.

Install

Add the sqlite_encrypted package from the SQLite.Encrypted Pub repository. Its exact federated dependencies and matching native libraries are resolved from that same repository.

dependencies:
  sqlite_encrypted:
    hosted: https://packages.sourcegear.com/pub
    version: <version>

Replace <version> with the SQLite.Encrypted Pub package version selected for your application.

Import the public entry point:

import 'package:sqlite_encrypted/sqlite_encrypted.dart';

The repository permits anonymous reads. A versioned ZIP remains available as an offline fallback; when using it, keep all package directories together and point a sqlite_encrypted path dependency at its sqlite_encrypted directory.

Desktop initialization

Windows, macOS, and Linux use sqflite's FFI factory. Select it before the first database operation. Android and iOS continue to use the ordinary sqflite plugin factory.

Desktop applications also add the matching FFI package from the same repository. Use the same version as sqlite_encrypted.

dependencies:
  sqflite_common_ffi:
    hosted: https://packages.sourcegear.com/pub
    version: <version>
import 'dart:io';

import 'package:sqlite_encrypted/sqlite_encrypted.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';

Future<void> main() async {
  if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
    sqfliteFfiInit();
    databaseFactory = databaseFactoryFfi;
  }

  await activateSee(processActivationKey);
  final database = await openDatabase(
    'application.db',
    key: databaseKey,
  );
}

The bundle carries x64 and ARM64 native assets for all three desktop operating systems. Dart's native-asset build selects and packages the correct SEE library; the application does not copy a DLL, dylib, or shared object manually.

Open an encrypted database

await activateSee(processActivationKey);

final database = await openDatabase(
  'application.db',
  key: databaseKey,
);

Activation may be repeated. Keyed open routes through the native key implementation and reports a failed key operation as a DatabaseException. Omit key for a plaintext database. Opening without key and immediately calling database.key() is the preferred alternative when the key is not available at open time.

Open with PRAGMA

Open without key, then execute both statements before schema access. Verify that the key PRAGMA returns one row containing ok.

import 'package:sqlite_encrypted/sqlite_encrypted.dart';

final database = await openDatabase('legacy.db');
await database.execute(
  "PRAGMA activate_extensions = 'see-<activation-key>'",
);
final keyRows = await database.rawQuery(
  "PRAGMA key = '<database-key>'",
);
if (keyRows.length != 1 || keyRows.single.values.single != 'ok') {
  throw StateError('SEE did not accept the database key');
}

On a build without encryption support, or when SEE cannot load the key, the key PRAGMA returns no row. Ignoring that result can leave the application using a connection whose encryption key was never applied. Prefer keyed open or database.key() for new integrations.

Create, insert, and query

The ordinary sqflite database API is unchanged.

await database.execute('''
  CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY,
    body TEXT NOT NULL
  )
''');

await database.insert('notes', {'body': 'hello'});

final notes = await database.query(
  'notes',
  columns: ['id', 'body'],
  orderBy: 'id',
);

Text, binary, and null keys

Dart does not overload methods by parameter type. Use key and rekey for nullable strings, and keyBytes and rekeyBytes for Uint8List.

import 'dart:typed_data';

await database.key(null, 'initial text key');

final binaryKey = Uint8List.fromList([0x01, 0x00, 0x7f, 0xff]);
await database.rekeyBytes(null, binaryKey);

await database.rekey(null, 'replacement text key');
await database.rekey(null, null);

Null removes encryption. Null database names and omitted codecs select the main database and the aes256ofb default.

await database.rekey(
  'main',
  'replacement key',
  codec: 'aes256ofb',
);

Open read-only

final readOnly = await openReadOnlyDatabase(
  'application.db',
  key: databaseKey,
);