| name | sqflite-android-setup |
| description | Use when configuring or troubleshooting the Android implementation of sqflite (package sqflite_android): when to add it explicitly, minSdk, SqfliteAndroid.registerWith, WAL via the com.tekartik.sqflite.wal_enabled manifest meta-data or setJournalMode, read-only opening and corruption handling, getDatabasesPath location, androidSetLocale / COLLATE LOCALIZED, the 1 MB CursorWindow row limit, background worker thread, Gradle/AGP build issues. |
sqflite_android: Android implementation of sqflite
sqflite_android is the endorsed Android implementation of the sqflite
federated plugin. Adding sqflite to a Flutter app pulls it in automatically
(default_package: sqflite_android); its SqfliteAndroid.registerWith()
installs the method channel DatabaseFactory as databaseFactory before
main() runs. It uses the SQLite shipped with the Android OS
(android.database.sqlite), so the SQLite version depends on the device.
dependencies:
sqflite:
Guidelines
- Add
sqflite_android explicitly only when the app does not depend on
sqflite (for example it codes against package:sqflite_common and ships
Android only). Then use databaseFactory / openDatabase from
package:sqflite_common/sqflite.dart; no call is needed, registration is
automatic. Android-only helpers such as androidSetLocale live in
package:sqflite/sqflite.dart, not here.
- Requirements:
minSdk 19, Java 17, Flutter >= 3.44 / Dart >= 3.12 (2.4.3
uses the Kotlin built into the Flutter Gradle plugin, AGP 9). If the build
fails with androidJdkImage or AGP errors, update the app's Gradle wrapper
and com.android.application plugin versions.
getDatabasesPath() returns data/data/<package>/databases; the plugin
creates the parent directory of the path on open. A relative path is
resolved there.
- SQL runs on a dedicated background worker thread (default
Process.THREAD_PRIORITY_DEFAULT); calls are serialized per database.
- WAL is disabled by default. Enable it globally with
<meta-data android:name="com.tekartik.sqflite.wal_enabled" android:value="true"/> inside <application> in
android/app/src/main/AndroidManifest.xml, or per open with
db.setJournalMode('WAL') in onConfigure (that extension falls back to
rawQuery because execute('PRAGMA journal_mode=WAL') fails on Android
when the manifest flag is not set).
readOnly: true opens with SQLiteDatabase.OPEN_READONLY and a
no-op corruption handler, so a corrupt or non-SQLite file is left intact
and the first access fails. A read-write open uses Android's default
handler which deletes a corrupt file.
db.androidSetLocale('fr-FR') (extension SqfliteDatabaseAndroidExt in
package:sqflite/sqflite.dart) sets the locale for ORDER BY name COLLATE LOCALIZED; call it in onConfigure at every open.
- Arguments are bound as strings;
SELECT ?1 returns '3' for [3],
comparisons and arithmetic still work.
- A single row must fit in the
CursorWindow (about 1 MB):
SQLiteBlobTooBigException / Row too big to fit into CursorWindow means
a blob should live in a file. java.lang.OutOfMemoryError on writes: split
into smaller transactions (for example 1000 operations each); reading:
limit columns and rows.
- Inspect a device database from Android Studio: Device File Explorer,
data/data/<package>/databases, Save As.
DatabaseException.getResultCode() returns the extended SQLite code on
Android (for example 2067 for a UNIQUE constraint), the primary code on
iOS; handle both.
MissingPluginException only in Android release mode: remove the
shrinkResources true and minifyEnabled true lines from the app
build.gradle.
Examples
Android-only app on the pure Dart API
dependencies:
flutter:
sdk: flutter
sqflite_common:
sqflite_android:
import 'package:path/path.dart';
import 'package:sqflite_common/sqflite.dart';
Future<Database> openAppDb() async {
// databaseFactory was registered by SqfliteAndroid.registerWith().
final path = join(await getDatabasesPath(), 'app.db');
return openDatabase(
path,
version: 1,
onConfigure: (db) => db.setJournalMode('WAL'),
onCreate: (db, _) =>
db.execute('CREATE TABLE Item (id INTEGER PRIMARY KEY, name TEXT)'),
);
}
Enabling WAL in the manifest
<application ...>
<meta-data
android:name="com.tekartik.sqflite.wal_enabled"
android:value="true" />
</application>
Localized sort (needs package:sqflite)
import 'package:sqflite/sqflite.dart';
Future<List<Map<String, Object?>>> sortedNames(String path) async {
final db = await openDatabase(
path,
version: 1,
onConfigure: (db) => db.androidSetLocale('zh-CN'),
onCreate: (db, _) => db.execute('CREATE TABLE Test(name TEXT)'),
);
return db.query('Test', orderBy: 'name COLLATE LOCALIZED ASC');
}
Common mistakes
- Adding both
sqflite and sqflite_android to pubspec.yaml: harmless
but redundant, sqflite already depends on it.
- Calling
execute('PRAGMA journal_mode=WAL') and getting an error: use
setJournalMode('WAL') or the manifest meta-data.
- Storing images or files as blobs and hitting the cursor window limit.
- Expecting JSON1 / UPSERT /
RETURNING on old Android versions; check
SELECT sqlite_version() or use sqflite_common_ffi for a bundled SQLite.
More
App-level API: the sqflite package skills (sqflite-open-database,
sqflite-crud-and-transactions, sqflite-testing-and-platforms). Other
implementation: sqflite_darwin. Interface: sqflite_platform_interface.