sqflite_platform_interface: writing a platform implementation
package:sqflite_platform_interface is the common interface of the sqflite
federated plugin. sqflite (the app-facing package) declares
sqflite_android and sqflite_darwin as default implementations; both are
SqflitePlatform subclasses whose registerWith() installs the method
channel DatabaseFactory as the global databaseFactory of
package:sqflite_common. Depend on this package only from an implementation
package; apps depend on sqflite.
import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';
/// Referenced as `dartPluginClass` in pubspec.yaml.
class SqfliteMyPlatform extends SqflitePlatform {
static void registerWith() {
// Native side speaks the com.tekartik.sqflite method channel protocol.
SqflitePlatform.initWithDatabaseFactoryMethodChannel();
}
}
Guidelines
- Extend
SqflitePlatform (it extends PlatformInterface with a private
token; implements is rejected by PlatformInterface.verify).
- Expose a static
registerWith() and declare it in the implementation
package pubspec.yaml under flutter: plugin: platforms: <platform>: dartPluginClass: <Class>, together with pluginClass (and package on
Android) for the native side. Flutter calls it before main().
- If your native code implements the sqflite method channel protocol
(channel
com.tekartik.sqflite, methods openDatabase, closeDatabase,
query, queryCursorNext, insert, update, execute, batch,
getDatabasesPath, databaseExists, deleteDatabase,
readDatabaseBytes, writeDatabaseBytes, options, debug), call
SqflitePlatform.initWithDatabaseFactoryMethodChannel() from
registerWith(). It sets databaseFactoryOrNull ??= SqflitePlatform.databaseFactoryMethodChannel, so an already registered
factory is kept. The wire format is described in
sqflite_common/doc/method_call_protocol.md.
- If the implementation is a Dart-side
DatabaseFactory (ffi, web,
in-memory), assign it with SqflitePlatform().databaseFactory = factory
or directly databaseFactory = factory from
package:sqflite_common/sqflite.dart. The factory must be a real sqflite
implementation: build it with the SqfliteDatabaseFactoryMixin /
buildDatabaseFactory(invokeMethod: ...) helpers that sqflite_common
exports for implementers (package:sqflite_common/src/mixin/import_mixin.dart,
an implementation import) so that openDatabase, versioning, transactions
and batches reuse the shared Dart logic. A plain class implementing
DatabaseFactory is rejected by the setter.
SqflitePlatform().databaseFactory (instance getter) returns the current
global factory; SqflitePlatform.databaseFactoryMethodChannel returns the
method channel one.
- Errors: the method channel factory converts a
PlatformException whose
code is sqlite_error into a DatabaseException; native code must use
that code and pass the SQL and arguments in details so
DatabaseException.toString() and getResultCode() work.
- Keep the package Flutter-only concerns (channels,
registerWith) here;
put anything reusable in sqflite_common.
Examples
pubspec.yaml of an implementation package
name: sqflite_myos
dependencies:
flutter:
sdk: flutter
sqflite_platform_interface: ">=2.4.1 <4.0.0"
sqflite_common: ">=2.5.9 <4.0.0"
flutter:
plugin:
implements: sqflite
platforms:
myos:
pluginClass: SqflitePlugin
dartPluginClass: SqfliteMyOs
Method channel implementation
import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';
class SqfliteMyOs extends SqflitePlatform {
/// Called by Flutter at startup (dartPluginClass).
static void registerWith() {
SqflitePlatform.initWithDatabaseFactoryMethodChannel();
}
}
Registering a Dart-side factory
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';
/// [factory] must come from an sqflite implementation (for example
/// databaseFactoryFfi from sqflite_common_ffi, or one built with
/// buildDatabaseFactory).
void registerDartFactory(DatabaseFactory factory) {
SqflitePlatform().databaseFactory = factory;
}
Reading the factory from the interface
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';
Future<Database> openThroughInterface(String path) {
final DatabaseFactory factory = SqflitePlatform().databaseFactory;
return factory.openDatabase(path);
}
Common mistakes
- Depending on
sqflite_platform_interface from an application; apps use
package:sqflite (or sqflite_common + an implementation).
- Forgetting
dartPluginClass in the pubspec, so registerWith() never runs
and openDatabase throws StateError: databaseFactory not initialized.
- Assigning a custom class that merely
implements DatabaseFactory:
ArgumentError: Unsupported sqflite factory.
- Returning errors with a code other than
sqlite_error from native code;
they surface as raw PlatformExceptions instead of DatabaseException.
More
Default implementations to copy from: sqflite_android (Kotlin/Java,
SqfliteAndroid) and sqflite_darwin (Objective-C, SqfliteDarwin,
sharedDarwinSource). App-level usage: the sqflite package skills.
1---2name: sqflite-platform-interface-implementers3description: Use when implementing or registering a platform implementation of the sqflite federated plugin (a new sqflite_<platform> package, a custom DatabaseFactory registered as the default): SqflitePlatform, SqflitePlatform.initWithDatabaseFactoryMethodChannel, databaseFactoryMethodChannel, the databaseFactory getter/setter, the dartPluginClass registerWith() hook, the com.tekartik.sqflite method channel and its method names (openDatabase, query, insert, update, execute, batch). Not for app code: apps use package:sqflite.4---56# sqflite_platform_interface: writing a platform implementation78`package:sqflite_platform_interface` is the common interface of the `sqflite`9federated plugin. `sqflite` (the app-facing package) declares10`sqflite_android` and `sqflite_darwin` as default implementations; both are11`SqflitePlatform` subclasses whose `registerWith()` installs the method12channel `DatabaseFactory` as the global `databaseFactory` of13`package:sqflite_common`. Depend on this package only from an implementation14package; apps depend on `sqflite`.1516```dart17import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';1819/// Referenced as `dartPluginClass` in pubspec.yaml.20class SqfliteMyPlatform extends SqflitePlatform {21 static void registerWith() {22 // Native side speaks the com.tekartik.sqflite method channel protocol.23 SqflitePlatform.initWithDatabaseFactoryMethodChannel();24 }25}26```2728## Guidelines2930* Extend `SqflitePlatform` (it extends `PlatformInterface` with a private31 token; `implements` is rejected by `PlatformInterface.verify`).32* Expose a static `registerWith()` and declare it in the implementation33 package `pubspec.yaml` under `flutter: plugin: platforms: <platform>:34 dartPluginClass: <Class>`, together with `pluginClass` (and `package` on35 Android) for the native side. Flutter calls it before `main()`.36* If your native code implements the sqflite method channel protocol37 (channel `com.tekartik.sqflite`, methods `openDatabase`, `closeDatabase`,38 `query`, `queryCursorNext`, `insert`, `update`, `execute`, `batch`,39 `getDatabasesPath`, `databaseExists`, `deleteDatabase`,40 `readDatabaseBytes`, `writeDatabaseBytes`, `options`, `debug`), call41 `SqflitePlatform.initWithDatabaseFactoryMethodChannel()` from42 `registerWith()`. It sets `databaseFactoryOrNull ??=43 SqflitePlatform.databaseFactoryMethodChannel`, so an already registered44 factory is kept. The wire format is described in45 `sqflite_common/doc/method_call_protocol.md`.46* If the implementation is a Dart-side `DatabaseFactory` (ffi, web,47 in-memory), assign it with `SqflitePlatform().databaseFactory = factory`48 or directly `databaseFactory = factory` from49 `package:sqflite_common/sqflite.dart`. The factory must be a real sqflite50 implementation: build it with the `SqfliteDatabaseFactoryMixin` /51 `buildDatabaseFactory(invokeMethod: ...)` helpers that `sqflite_common`52 exports for implementers (`package:sqflite_common/src/mixin/import_mixin.dart`,53 an implementation import) so that `openDatabase`, versioning, transactions54 and batches reuse the shared Dart logic. A plain class implementing55 `DatabaseFactory` is rejected by the setter.56* `SqflitePlatform().databaseFactory` (instance getter) returns the current57 global factory; `SqflitePlatform.databaseFactoryMethodChannel` returns the58 method channel one.59* Errors: the method channel factory converts a `PlatformException` whose60 `code` is `sqlite_error` into a `DatabaseException`; native code must use61 that code and pass the SQL and arguments in `details` so62 `DatabaseException.toString()` and `getResultCode()` work.63* Keep the package Flutter-only concerns (channels, `registerWith`) here;64 put anything reusable in `sqflite_common`.6566## Examples6768### pubspec.yaml of an implementation package6970```yaml71name: sqflite_myos72dependencies:73 flutter:74 sdk: flutter75 sqflite_platform_interface: ">=2.4.1 <4.0.0"76 sqflite_common: ">=2.5.9 <4.0.0"7778flutter:79 plugin:80 implements: sqflite81 platforms:82 myos:83 pluginClass: SqflitePlugin84 dartPluginClass: SqfliteMyOs85```8687### Method channel implementation8889```dart90import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';9192class SqfliteMyOs extends SqflitePlatform {93 /// Called by Flutter at startup (dartPluginClass).94 static void registerWith() {95 SqflitePlatform.initWithDatabaseFactoryMethodChannel();96 }97}98```99100### Registering a Dart-side factory101102```dart103import 'package:sqflite_common/sqlite_api.dart';104import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';105106/// [factory] must come from an sqflite implementation (for example107/// databaseFactoryFfi from sqflite_common_ffi, or one built with108/// buildDatabaseFactory).109void registerDartFactory(DatabaseFactory factory) {110 SqflitePlatform().databaseFactory = factory;111}112```113114### Reading the factory from the interface115116```dart117import 'package:sqflite_common/sqlite_api.dart';118import 'package:sqflite_platform_interface/sqflite_platform_interface.dart';119120Future<Database> openThroughInterface(String path) {121 final DatabaseFactory factory = SqflitePlatform().databaseFactory;122 return factory.openDatabase(path);123}124```125126## Common mistakes127128* Depending on `sqflite_platform_interface` from an application; apps use129 `package:sqflite` (or `sqflite_common` + an implementation).130* Forgetting `dartPluginClass` in the pubspec, so `registerWith()` never runs131 and `openDatabase` throws `StateError: databaseFactory not initialized`.132* Assigning a custom class that merely `implements DatabaseFactory`:133 `ArgumentError: Unsupported sqflite factory`.134* Returning errors with a code other than `sqlite_error` from native code;135 they surface as raw `PlatformException`s instead of `DatabaseException`.136137## More138139Default implementations to copy from: `sqflite_android` (Kotlin/Java,140`SqfliteAndroid`) and `sqflite_darwin` (Objective-C, `SqfliteDarwin`,141`sharedDarwinSource`). App-level usage: the `sqflite` package skills.