sqflite_common_ffi: sqflite on desktop and the Dart VM
package:sqflite_common_ffi is a DatabaseFactory implementation of the
sqflite API built on package:sqlite3 (dart:ffi). It runs SQLite in a
background isolate and works on Linux, macOS and Windows, on the Dart VM and in
Flutter (also on iOS/Android through sqlite3 build hooks). It is the only way
to use sqflite in a pure Dart program and the standard way to unit test sqflite
code (see the sqflite-common-ffi-testing skill).
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<void> main() async {
sqfliteFfiInit(); // Windows setup, no-op elsewhere
var db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);
await db.execute('CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)');
await db.insert('Product', {'title': 'Product 1'});
print(await db.query('Product')); // [{id: 1, title: Product 1}]
await db.close();
}
The Database, Transaction, Batch and OpenDatabaseOptions API is the
sqflite one and is documented by the sqflite / sqflite_common package
skills (sqflite-open-database, sqflite-crud-and-transactions,
sqflite-common-api). This skill only covers the ffi factory.
Guidelines
Dependency and import
- Add
sqflite_common_ffi: ^2.4.2 to dependencies (or dev_dependencies
when it is only used by tests). It requires Dart 3.12 and sqlite3 >= 3.
- Import
package:sqflite_common_ffi/sqflite_ffi.dart. It re-exports
package:sqflite_common/sqflite.dart, so Database, DatabaseFactory,
OpenDatabaseOptions, inMemoryDatabasePath, DatabaseException, the
global databaseFactory setter and the global openDatabase() /
deleteDatabase() functions are all available from this single import.
- In a pure Dart project never import
package:sqflite/sqflite.dart (Flutter
only). In a Flutter project either import works; prefer sqflite_common_ffi
in code shared with tests.
- Public API of the package:
sqfliteFfiInit, databaseFactoryFfi,
databaseFactoryFfiNoIsolate, createDatabaseFactoryFfi, the SqfliteFfiInit
typedef (void Function()) and SqfliteFfiIsolatePortServer. Nothing under
src/ is meant to be imported.
Initialization
- Call
sqfliteFfiInit() once in main() (or setUpAll) before the first
database call. It only does Windows-specific work (loads the library in the
main isolate by opening and closing an in-memory database) and is a no-op on
other platforms and on the web, so it is safe to call unconditionally.
databaseFactoryFfi is a global DatabaseFactory (tag ffi). Every call
is sent to one background SqfliteIsolate spawned lazily on first use and
shared by all ffi factories of the current Dart isolate.
databaseFactoryFfiNoIsolate runs SQLite synchronously in the calling
isolate. Use it inside a background isolate you already own
(Isolate.run, compute) or in Flutter widget tests. In the main isolate of
a UI app a long query blocks the UI. It throws UnimplementedError on the
web.
createDatabaseFactoryFfi({SqfliteFfiInit? ffiInit, bool noIsolate = false, SqfliteFfiIsolatePortServer? isolatePortServer}) builds a custom factory:
ffiInit is a top-level or static void Function() executed inside the
sqflite isolate before the first SQLite call (with noIsolate: true, in
the current isolate at the first call). Use it for extra native setup;
the library itself is selected by sqlite3 build hooks, see below.
isolatePortServer shares the sqflite isolate SendPort between Dart
isolates (lookupPort(), registerPort(SendPort), unregisterPort()).
In Flutter use the sqflite_ffi package, which implements it with
IsolateNameServer; implement it yourself only for a custom registry.
- Setting the global factory:
databaseFactory = databaseFactoryFfi; once,
before any global openDatabase(). Only needed for code (yours or third
party) that uses the global openDatabase / deleteDatabase functions.
Reading databaseFactory before it is set throws StateError; setting it a
second time prints a warning. Prefer passing a DatabaseFactory to your
own classes so tests can inject any factory.
- Flutter app targeting desktop: set the factory on Windows/Linux (see
example) or simply depend on
sqflite_ffi, a Dart-only plugin that
registers the ffi factory automatically and shares the isolate between
Flutter isolates.
Paths
inMemoryDatabasePath (:memory:) opens a private in-memory database.
After close() the data is gone.
- A relative path is joined with
getDatabasesPath(), which for ffi is
<current directory>/.dart_tool/sqflite_common_ffi/databases. This is fine
for scripts and tests, not for an installed application: build an absolute
path yourself (path_provider getApplicationSupportDirectory() in
Flutter, or a directory you control) and join it with package:path.
- The parent directory of the database file is created on open when the file
does not exist.
file: URIs are passed through to SQLite (uri: true).
readOnly: true on a missing file throws instead of creating it.
deleteDatabase(path) closes the single instance and removes the file and
its journal files. databaseExists(path) checks the file.
readDatabaseBytes(path) / writeDatabaseBytes(path, bytes) on the
factory export or import a whole database file.
Native SQLite library (sqlite3 v3, build hooks)
sqflite_common_ffi >= 2.4 uses sqlite3 >= 3, which downloads and
bundles SQLite with Dart build hooks: no libsqlite3-dev on Linux, no
sqlite3.dll to copy on Windows.
Hooks run with dart run <file> / dart test / flutter run / flutter build, not with dart <file>. Run from the command line at least once so
the library is built (IDE launchers may skip hooks). Run flutter clean
after changing the sqlite3 major version.
The library is chosen in pubspec.yaml, not in Dart code:
hooks:
user_defines:
sqlite3:
source: system # sqlite3 (default, bundled), sqlcipher, sqlite3mc, system, process, executable
source: sqlcipher or source: sqlite3mc give encryption: send
PRAGMA key = '...' as the first statement in onConfigure.
package:sqlite3/open.dart (open.overrideFor) belongs to sqlite3 v2 and
no longer exists; do not write ffiInit functions that use it. To stay on
v2 pin sqflite_common_ffi: ^2.3.7 with sqlite3: ^2.9.4 (see
references/native-library.md).
Behavior notes
- Exceptions are
DatabaseException (from sqflite_common): use
isNoSuchTableError(), isUniqueConstraintError(), isSyntaxError(),
isDatabaseClosedError(), getResultCode() rather than parsing messages.
- SQLite defensive mode is on by default.
PRAGMA writable_schema = ON only
works after await db.execute('PRAGMA sqflite -- db_config_defensive_off')
(constant sqflitePragmaDbDefensiveOff in
package:sqflite_common/utils/utils.dart).
- Each Dart isolate has its own sqflite isolate and connections, so
singleInstance does not span Dart isolates unless an
isolatePortServer is used (sqflite_ffi in Flutter). When two isolates
open the same file, pass rollbackActiveTransactionOnOpen: false in
OpenDatabaseOptions so one isolate does not roll back the other's
transaction on open.
singleInstance: false (multi-instance) is simulated, keep the default.
- Web:
databaseFactoryFfi throws UnsupportedError; use
databaseFactoryFfiWeb from sqflite_common_ffi_web (needs sqlite3.wasm
and a worker file in web/). For a readTransaction / concurrent-read
variant on desktop see sqflite_common_ffi_async.
Examples
Dart CLI application with a persistent database
import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<void> main() async {
sqfliteFfiInit();
var factory = databaseFactoryFfi;
// Absolute path you control; the parent directory is created on open.
var path = p.join(Directory.current.path, 'data', 'app.db');
var db = await factory.openDatabase(
path,
options: OpenDatabaseOptions(
version: 1,
onCreate: (db, version) async {
await db.execute(
'CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)',
);
},
),
);
await db.insert('Product', {'title': 'Product ${DateTime.now()}'});
for (var row in await db.query('Product')) {
print(row);
}
await db.close();
}
Flutter app: use ffi on Windows and Linux, the sqflite plugin elsewhere
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isWindows || Platform.isLinux) {
sqfliteFfiInit();
// Global openDatabase() now works on desktop.
databaseFactory = databaseFactoryFfi;
}
// iOS/Android/macOS keep the native sqflite plugin factory.
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => const SizedBox();
}
For an app that should use ffi on every platform, depend on sqflite_ffi
instead of doing this by hand.
Injecting the factory instead of using the global one
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class ProductStore {
ProductStore(this.factory, this.path);
final DatabaseFactory factory;
final String path;
Database? _db;
Future<Database> get db async => _db ??= await factory.openDatabase(
path,
options: OpenDatabaseOptions(
version: 1,
onCreate: (db, _) => db.execute(
'CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)',
),
),
);
Future<int> add(String title) async =>
(await db).insert('Product', {'title': title});
Future<void> close() async {
await _db?.close();
_db = null;
}
}
Future<void> main() async {
sqfliteFfiInit();
var store = ProductStore(databaseFactoryFfi, 'products.db');
await store.add('Pen');
await store.close();
}
Database work inside a background isolate (no extra sqflite isolate)
import 'dart:isolate';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<int> countRows(String path) => Isolate.run(() async {
// Already in a background isolate: run SQLite synchronously here.
var db = await databaseFactoryFfiNoIsolate.openDatabase(path);
try {
var rows = await db.rawQuery('SELECT COUNT(*) AS c FROM Product');
return rows.first['c'] as int;
} finally {
await db.close();
}
});
Custom factory with an ffiInit hook
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
// Must be top-level or static: it is sent to the sqflite isolate.
void _ffiInit() {
// Runs in the sqflite isolate before any SQLite call.
}
final databaseFactoryCustom = createDatabaseFactoryFfi(ffiInit: _ffiInit);
Encrypted database (SQLCipher build selected in pubspec.yaml)
# pubspec.yaml
hooks:
user_defines:
sqlite3:
source: sqlcipher
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<Database> openEncrypted(String path, String key) =>
databaseFactoryFfi.openDatabase(
path,
options: OpenDatabaseOptions(
version: 1,
onConfigure: (db) async {
// First statement on the connection.
await db.rawQuery("PRAGMA key = '$key'");
},
onCreate: (db, _) => db.execute('CREATE TABLE t (i INTEGER)'),
),
);
Editing sqlite_master (defensive mode off)
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<void> renameTypeInSchema(Database db) async {
await db.execute('PRAGMA sqflite -- db_config_defensive_off');
await db.execute('PRAGMA writable_schema = ON');
await db.update(
'sqlite_master',
{'sql': 'CREATE TABLE Test(value BLOB)'},
where: "name = 'Test' AND type = 'table'",
);
}
Common mistakes
- Using
databaseFactoryFfi on the web (throws UnsupportedError). Use
sqflite_common_ffi_web.
- Calling the global
openDatabase() without databaseFactory = databaseFactoryFfi; first: StateError: databaseFactory not initialized.
- Passing a relative path in an installed app: it lands in
.dart_tool/sqflite_common_ffi/databases under the current directory.
- Running
dart bin/main.dart and getting a "failed to load dynamic library"
error: use dart run bin/main.dart so build hooks run.
- Writing an
ffiInit with package:sqlite3/open.dart (v2 API). Select the
library with hooks.user_defines.sqlite3 in pubspec.yaml.
- Passing a closure or instance method as
ffiInit: it must be top-level or
static to cross the isolate boundary.
- Expecting
singleInstance to be shared across Dart isolates. Use
sqflite_ffi (Flutter) or a SqfliteFfiIsolatePortServer.
- Importing
package:sqflite/sqflite.dart in a Dart VM program.
More
See references/native-library.md for the
sqlite3 hook options, the legacy sqlite3 v2 setup (Linux libsqlite3-dev,
Windows dll, open.overrideFor), CI notes and troubleshooting.
1---2name: sqflite-common-ffi-desktop3description: Use when running sqflite on Windows, Linux or macOS (Dart VM, CLI, server or Flutter desktop) with package:sqflite_common_ffi: sqfliteFfiInit, databaseFactoryFfi, databaseFactoryFfiNoIsolate, createDatabaseFactoryFfi (ffiInit, noIsolate, isolatePortServer, SqfliteFfiIsolatePortServer), setting the global databaseFactory so openDatabase works on desktop, inMemoryDatabasePath, database paths and getDatabasesPath, the sqflite background isolate, the sqlite3 native library and build hooks (system library, SQLCipher, sqlite3mc), custom "PRAGMA sqflite" statements, and which companion package to use on the web (sqflite_common_ffi_web) or in a Flutter app (sqflite_ffi).4---56# sqflite_common_ffi: sqflite on desktop and the Dart VM78`package:sqflite_common_ffi` is a `DatabaseFactory` implementation of the9sqflite API built on `package:sqlite3` (dart:ffi). It runs SQLite in a10background isolate and works on Linux, macOS and Windows, on the Dart VM and in11Flutter (also on iOS/Android through `sqlite3` build hooks). It is the only way12to use sqflite in a pure Dart program and the standard way to unit test sqflite13code (see the `sqflite-common-ffi-testing` skill).1415```dart16import 'package:sqflite_common_ffi/sqflite_ffi.dart';1718Future<void> main() async {19 sqfliteFfiInit(); // Windows setup, no-op elsewhere20 var db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);21 await db.execute('CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)');22 await db.insert('Product', {'title': 'Product 1'});23 print(await db.query('Product')); // [{id: 1, title: Product 1}]24 await db.close();25}26```2728The `Database`, `Transaction`, `Batch` and `OpenDatabaseOptions` API is the29sqflite one and is documented by the `sqflite` / `sqflite_common` package30skills (`sqflite-open-database`, `sqflite-crud-and-transactions`,31`sqflite-common-api`). This skill only covers the ffi factory.3233## Guidelines3435### Dependency and import3637* Add `sqflite_common_ffi: ^2.4.2` to `dependencies` (or `dev_dependencies`38 when it is only used by tests). It requires Dart 3.12 and `sqlite3 >= 3`.39* Import `package:sqflite_common_ffi/sqflite_ffi.dart`. It re-exports40 `package:sqflite_common/sqflite.dart`, so `Database`, `DatabaseFactory`,41 `OpenDatabaseOptions`, `inMemoryDatabasePath`, `DatabaseException`, the42 global `databaseFactory` setter and the global `openDatabase()` /43 `deleteDatabase()` functions are all available from this single import.44* In a pure Dart project never import `package:sqflite/sqflite.dart` (Flutter45 only). In a Flutter project either import works; prefer `sqflite_common_ffi`46 in code shared with tests.47* Public API of the package: `sqfliteFfiInit`, `databaseFactoryFfi`,48 `databaseFactoryFfiNoIsolate`, `createDatabaseFactoryFfi`, the `SqfliteFfiInit`49 typedef (`void Function()`) and `SqfliteFfiIsolatePortServer`. Nothing under50 `src/` is meant to be imported.5152### Initialization5354* Call `sqfliteFfiInit()` once in `main()` (or `setUpAll`) before the first55 database call. It only does Windows-specific work (loads the library in the56 main isolate by opening and closing an in-memory database) and is a no-op on57 other platforms and on the web, so it is safe to call unconditionally.58* `databaseFactoryFfi` is a global `DatabaseFactory` (tag `ffi`). Every call59 is sent to one background `SqfliteIsolate` spawned lazily on first use and60 shared by all ffi factories of the current Dart isolate.61* `databaseFactoryFfiNoIsolate` runs SQLite synchronously in the calling62 isolate. Use it inside a background isolate you already own63 (`Isolate.run`, `compute`) or in Flutter widget tests. In the main isolate of64 a UI app a long query blocks the UI. It throws `UnimplementedError` on the65 web.66* `createDatabaseFactoryFfi({SqfliteFfiInit? ffiInit, bool noIsolate = false,67 SqfliteFfiIsolatePortServer? isolatePortServer})` builds a custom factory:68 * `ffiInit` is a top-level or static `void Function()` executed inside the69 sqflite isolate before the first SQLite call (with `noIsolate: true`, in70 the current isolate at the first call). Use it for extra native setup;71 the library itself is selected by `sqlite3` build hooks, see below.72 * `isolatePortServer` shares the sqflite isolate `SendPort` between Dart73 isolates (`lookupPort()`, `registerPort(SendPort)`, `unregisterPort()`).74 In Flutter use the `sqflite_ffi` package, which implements it with75 `IsolateNameServer`; implement it yourself only for a custom registry.76* Setting the global factory: `databaseFactory = databaseFactoryFfi;` once,77 before any global `openDatabase()`. Only needed for code (yours or third78 party) that uses the global `openDatabase` / `deleteDatabase` functions.79 Reading `databaseFactory` before it is set throws `StateError`; setting it a80 second time prints a warning. Prefer passing a `DatabaseFactory` to your81 own classes so tests can inject any factory.82* Flutter app targeting desktop: set the factory on Windows/Linux (see83 example) or simply depend on `sqflite_ffi`, a Dart-only plugin that84 registers the ffi factory automatically and shares the isolate between85 Flutter isolates.8687### Paths8889* `inMemoryDatabasePath` (`:memory:`) opens a private in-memory database.90 After `close()` the data is gone.91* A relative path is joined with `getDatabasesPath()`, which for ffi is92 `<current directory>/.dart_tool/sqflite_common_ffi/databases`. This is fine93 for scripts and tests, not for an installed application: build an absolute94 path yourself (`path_provider` `getApplicationSupportDirectory()` in95 Flutter, or a directory you control) and join it with `package:path`.96* The parent directory of the database file is created on open when the file97 does not exist. `file:` URIs are passed through to SQLite (`uri: true`).98* `readOnly: true` on a missing file throws instead of creating it.99* `deleteDatabase(path)` closes the single instance and removes the file and100 its journal files. `databaseExists(path)` checks the file.101 `readDatabaseBytes(path)` / `writeDatabaseBytes(path, bytes)` on the102 factory export or import a whole database file.103104### Native SQLite library (sqlite3 v3, build hooks)105106* `sqflite_common_ffi >= 2.4` uses `sqlite3 >= 3`, which downloads and107 bundles SQLite with Dart build hooks: no `libsqlite3-dev` on Linux, no108 `sqlite3.dll` to copy on Windows.109* Hooks run with `dart run <file>` / `dart test` / `flutter run` / `flutter110 build`, not with `dart <file>`. Run from the command line at least once so111 the library is built (IDE launchers may skip hooks). Run `flutter clean`112 after changing the `sqlite3` major version.113* The library is chosen in `pubspec.yaml`, not in Dart code:114115 ```yaml116 hooks:117 user_defines:118 sqlite3:119 source: system # sqlite3 (default, bundled), sqlcipher, sqlite3mc, system, process, executable120 ```121122 `source: sqlcipher` or `source: sqlite3mc` give encryption: send123 `PRAGMA key = '...'` as the first statement in `onConfigure`.124* `package:sqlite3/open.dart` (`open.overrideFor`) belongs to `sqlite3` v2 and125 no longer exists; do not write `ffiInit` functions that use it. To stay on126 v2 pin `sqflite_common_ffi: ^2.3.7` with `sqlite3: ^2.9.4` (see127 [references/native-library.md](references/native-library.md)).128129### Behavior notes130131* Exceptions are `DatabaseException` (from `sqflite_common`): use132 `isNoSuchTableError()`, `isUniqueConstraintError()`, `isSyntaxError()`,133 `isDatabaseClosedError()`, `getResultCode()` rather than parsing messages.134* SQLite defensive mode is on by default. `PRAGMA writable_schema = ON` only135 works after `await db.execute('PRAGMA sqflite -- db_config_defensive_off')`136 (constant `sqflitePragmaDbDefensiveOff` in137 `package:sqflite_common/utils/utils.dart`).138* Each Dart isolate has its own sqflite isolate and connections, so139 `singleInstance` does not span Dart isolates unless an140 `isolatePortServer` is used (`sqflite_ffi` in Flutter). When two isolates141 open the same file, pass `rollbackActiveTransactionOnOpen: false` in142 `OpenDatabaseOptions` so one isolate does not roll back the other's143 transaction on open.144* `singleInstance: false` (multi-instance) is simulated, keep the default.145* Web: `databaseFactoryFfi` throws `UnsupportedError`; use146 `databaseFactoryFfiWeb` from `sqflite_common_ffi_web` (needs `sqlite3.wasm`147 and a worker file in `web/`). For a `readTransaction` / concurrent-read148 variant on desktop see `sqflite_common_ffi_async`.149150## Examples151152### Dart CLI application with a persistent database153154```dart155import 'dart:io';156157import 'package:path/path.dart' as p;158import 'package:sqflite_common_ffi/sqflite_ffi.dart';159160Future<void> main() async {161 sqfliteFfiInit();162 var factory = databaseFactoryFfi;163164 // Absolute path you control; the parent directory is created on open.165 var path = p.join(Directory.current.path, 'data', 'app.db');166 var db = await factory.openDatabase(167 path,168 options: OpenDatabaseOptions(169 version: 1,170 onCreate: (db, version) async {171 await db.execute(172 'CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)',173 );174 },175 ),176 );177 await db.insert('Product', {'title': 'Product ${DateTime.now()}'});178 for (var row in await db.query('Product')) {179 print(row);180 }181 await db.close();182}183```184185### Flutter app: use ffi on Windows and Linux, the sqflite plugin elsewhere186187```dart188import 'dart:io';189190import 'package:flutter/widgets.dart';191import 'package:sqflite_common_ffi/sqflite_ffi.dart';192193Future<void> main() async {194 WidgetsFlutterBinding.ensureInitialized();195 if (Platform.isWindows || Platform.isLinux) {196 sqfliteFfiInit();197 // Global openDatabase() now works on desktop.198 databaseFactory = databaseFactoryFfi;199 }200 // iOS/Android/macOS keep the native sqflite plugin factory.201 runApp(const MyApp());202}203204class MyApp extends StatelessWidget {205 const MyApp({super.key});206 @override207 Widget build(BuildContext context) => const SizedBox();208}209```210211For an app that should use ffi on every platform, depend on `sqflite_ffi`212instead of doing this by hand.213214### Injecting the factory instead of using the global one215216```dart217import 'package:sqflite_common_ffi/sqflite_ffi.dart';218219class ProductStore {220 ProductStore(this.factory, this.path);221 final DatabaseFactory factory;222 final String path;223 Database? _db;224225 Future<Database> get db async => _db ??= await factory.openDatabase(226 path,227 options: OpenDatabaseOptions(228 version: 1,229 onCreate: (db, _) => db.execute(230 'CREATE TABLE Product (id INTEGER PRIMARY KEY, title TEXT)',231 ),232 ),233 );234235 Future<int> add(String title) async =>236 (await db).insert('Product', {'title': title});237238 Future<void> close() async {239 await _db?.close();240 _db = null;241 }242}243244Future<void> main() async {245 sqfliteFfiInit();246 var store = ProductStore(databaseFactoryFfi, 'products.db');247 await store.add('Pen');248 await store.close();249}250```251252### Database work inside a background isolate (no extra sqflite isolate)253254```dart255import 'dart:isolate';256257import 'package:sqflite_common_ffi/sqflite_ffi.dart';258259Future<int> countRows(String path) => Isolate.run(() async {260 // Already in a background isolate: run SQLite synchronously here.261 var db = await databaseFactoryFfiNoIsolate.openDatabase(path);262 try {263 var rows = await db.rawQuery('SELECT COUNT(*) AS c FROM Product');264 return rows.first['c'] as int;265 } finally {266 await db.close();267 }268 });269```270271### Custom factory with an ffiInit hook272273```dart274import 'package:sqflite_common_ffi/sqflite_ffi.dart';275276// Must be top-level or static: it is sent to the sqflite isolate.277void _ffiInit() {278 // Runs in the sqflite isolate before any SQLite call.279}280281final databaseFactoryCustom = createDatabaseFactoryFfi(ffiInit: _ffiInit);282```283284### Encrypted database (SQLCipher build selected in pubspec.yaml)285286```yaml287# pubspec.yaml288hooks:289 user_defines:290 sqlite3:291 source: sqlcipher292```293294```dart295import 'package:sqflite_common_ffi/sqflite_ffi.dart';296297Future<Database> openEncrypted(String path, String key) =>298 databaseFactoryFfi.openDatabase(299 path,300 options: OpenDatabaseOptions(301 version: 1,302 onConfigure: (db) async {303 // First statement on the connection.304 await db.rawQuery("PRAGMA key = '$key'");305 },306 onCreate: (db, _) => db.execute('CREATE TABLE t (i INTEGER)'),307 ),308 );309```310311### Editing sqlite_master (defensive mode off)312313```dart314import 'package:sqflite_common_ffi/sqflite_ffi.dart';315316Future<void> renameTypeInSchema(Database db) async {317 await db.execute('PRAGMA sqflite -- db_config_defensive_off');318 await db.execute('PRAGMA writable_schema = ON');319 await db.update(320 'sqlite_master',321 {'sql': 'CREATE TABLE Test(value BLOB)'},322 where: "name = 'Test' AND type = 'table'",323 );324}325```326327## Common mistakes328329* Using `databaseFactoryFfi` on the web (throws `UnsupportedError`). Use330 `sqflite_common_ffi_web`.331* Calling the global `openDatabase()` without `databaseFactory =332 databaseFactoryFfi;` first: `StateError: databaseFactory not initialized`.333* Passing a relative path in an installed app: it lands in334 `.dart_tool/sqflite_common_ffi/databases` under the current directory.335* Running `dart bin/main.dart` and getting a "failed to load dynamic library"336 error: use `dart run bin/main.dart` so build hooks run.337* Writing an `ffiInit` with `package:sqlite3/open.dart` (v2 API). Select the338 library with `hooks.user_defines.sqlite3` in `pubspec.yaml`.339* Passing a closure or instance method as `ffiInit`: it must be top-level or340 static to cross the isolate boundary.341* Expecting `singleInstance` to be shared across Dart isolates. Use342 `sqflite_ffi` (Flutter) or a `SqfliteFfiIsolatePortServer`.343* Importing `package:sqflite/sqflite.dart` in a Dart VM program.344345## More346347See [references/native-library.md](references/native-library.md) for the348`sqlite3` hook options, the legacy `sqlite3` v2 setup (Linux `libsqlite3-dev`,349Windows dll, `open.overrideFor`), CI notes and troubleshooting.