Agent Skillstekartik/sqflite › sqflite-common-api

sqflite-common-api

GitHub

提供纯 Dart 的 sqflite API,支持跨平台 SQLite 操作。用于编写不依赖 Flutter 的代码或需兼容多端的共享包,涵盖数据库工厂、事务及 SQL 执行等核心类型与工具。

sqflite_common/skills/sqflite-common-api/SKILL.md tekartik/sqflite

Trigger Scenarios

编写跨平台 Dart 代码 使用 sqflite_common API

Install

npx skills add tekartik/sqflite --skill sqflite-common-api -g -y
More Options

Non-standard path

npx skills add https://github.com/tekartik/sqflite/tree/master/sqflite_common/skills/sqflite-common-api -g -y

Use without installing

npx skills use tekartik/sqflite@sqflite-common-api

指定 Agent (Claude Code)

npx skills add tekartik/sqflite --skill sqflite-common-api -a claude-code -g -y

安装 repo 全部 skill

npx skills add tekartik/sqflite --all -g -y

预览 repo 内 skill

npx skills add tekartik/sqflite --list

SKILL.md

Frontmatter
{
    "name": "sqflite-common-api",
    "description": "Use when writing Dart code against the sqflite API without depending on Flutter, or when a shared package must work with sqflite (mobile), sqflite_common_ffi (desktop\/VM\/tests) and sqflite_common_ffi_web: the DatabaseFactory, Database, Transaction, Batch, DatabaseExecutor, OpenDatabaseOptions, ConflictAlgorithm, DatabaseException, QueryCursor types from package:sqflite_common\/sqlite_api.dart, the global databaseFactory \/ openDatabase from sqflite_common\/sqflite.dart, sql.dart escapeName, utils\/utils.dart firstIntValue, sqflite_logger.dart SqfliteDatabaseFactoryLogger, sandbox(), SqfliteSqlCommand, queryIterate."
}

sqflite_common: the pure-Dart sqflite API

package:sqflite_common defines the whole sqflite API (DatabaseFactory, Database, Transaction, Batch, OpenDatabaseOptions, ...) and the shared implementation, with no Flutter dependency. It contains no SQLite engine: an implementation package supplies a DatabaseFactory (sqflite on Android/iOS/macOS, sqflite_common_ffi on desktop/VM/tests, sqflite_common_ffi_web on the web). Depend on sqflite_common in packages that must stay Flutter-free and accept a factory from the caller.

import 'package:sqflite_common/sqlite_api.dart';

class NoteStore {
  NoteStore(this.factory, this.path);

  final DatabaseFactory factory;
  final String path;

  Future<Database> open() => factory.openDatabase(
        path,
        options: OpenDatabaseOptions(
          version: 1,
          onCreate: (db, _) => db.execute(
            'CREATE TABLE Note (id INTEGER PRIMARY KEY, content TEXT)',
          ),
        ),
      );
}

Guidelines

Which import gives what

  • package:sqflite_common/sqlite_api.dart: the types only. DatabaseFactory (openDatabase(path, {options}), getDatabasesPath(), setDatabasesPath(), deleteDatabase(), databaseExists(), readDatabaseBytes(), writeDatabaseBytes()), DatabaseExecutor (execute, rawInsert/insert, rawQuery/query, rawQueryCursor/queryCursor, rawUpdate/update, rawDelete/delete, batch(), database), Database (path, isOpen, close(), transaction(), readTransaction()), Transaction, Batch (commit, apply, length, same mutators as the executor), QueryCursor, OpenDatabaseOptions, ConflictAlgorithm, DatabaseException, inMemoryDatabasePath, the callback typedefs (OnDatabaseCreateFn, OnDatabaseVersionChangeFn, OnDatabaseOpenFn, OnDatabaseConfigureFn), onDatabaseDowngradeDelete, onDatabaseVersionChangeError, and the extensions SqfliteDatabaseExecutorExt (getVersion, setVersion), SqfliteDatabaseExt (setJournalMode), SqfliteDatabaseExecutorIterateExt (queryIterate, rawQueryIterate), SqfliteDatabaseFactorySandboxExtension (sandbox), SqfliteSqlCommand + SqfliteSqlCommandExecutorExt, sqfliteLogLevel* constants. Use this import in libraries.
  • package:sqflite_common/sqflite.dart: everything above plus the global databaseFactory getter/setter, databaseFactoryOrNull, and the global functions openDatabase, openReadOnlyDatabase, getDatabasesPath, deleteDatabase, databaseExists that forward to databaseFactory. Only applications should rely on the global; a library that reads it forces the app to have set it. package:sqflite/sqflite.dart and package:sqflite_common_ffi/sqflite_ffi.dart both re-export this library, so do not import it next to them (unnecessary_import).
  • package:sqflite_common/sql.dart: ConflictAlgorithm, escapeName, unescapeName.
  • package:sqflite_common/utils/utils.dart: firstIntValue(rows), firstStringValue(rows), hex(bytes), setLockWarningInfo(duration:, callback:), sqlCountColumn ('COUNT(*)'). There is no Sqflite class here; that class lives in package:sqflite.
  • package:sqflite_common/sqflite_logger.dart: SqfliteDatabaseFactoryLogger (its constructor is @experimental, expect an experimental_member_use analyzer warning), SqfliteLoggerOptions, SqfliteDatabaseFactoryLoggerType (all, invoke), the event classes (SqfliteLoggerSqlEvent, SqfliteLoggerBatchEvent, SqfliteLoggerDatabaseOpenEvent, SqfliteLoggerDatabaseCloseEvent, SqfliteLoggerDatabaseDeleteEvent, SqfliteLoggerInvokeEvent), SqfliteLoggerEventExt.dump() and DatabaseFactoryLoggerDebugExt.debugQuickLoggerWrapper().
  • package:sqflite_common/sqflite_dev.dart: deprecated dev-only setLogLevel / setOptions (SqfliteDatabaseFactoryDev) and SqfliteOptions. Do not ship code that uses it.
  • Never import package:sqflite_common/src/... from user code.

Designing a Flutter-free package

  • Take a DatabaseFactory (or a Database) as a constructor or function parameter. The Flutter app passes databaseFactory from package:sqflite, the CLI or test passes databaseFactoryFfi from sqflite_common_ffi.
  • Take the database path as a parameter too: getDatabasesPath() is only meaningful for the sqflite plugin; on other implementations the app should compute a location (path_provider, a CLI argument, a temp dir). inMemoryDatabasePath (':memory:') works everywhere.
  • Type your SQL-facing functions on DatabaseExecutor so they work with both a Database and a Transaction.
  • Opening returns the same instance for the same path while singleInstance is true (default); it is forced to false for in-memory databases.
  • databaseFactory throws StateError('databaseFactory not initialized') until an implementation sets it; the setter rejects factories that are not sqflite implementations (ArgumentError) and prints a warning when changing an already set factory.
  • factory.sandbox(path: root) returns a DatabaseFactory restricted to root (relative paths resolved under it, absolute paths must be inside, getDatabasesPath() returns root); it works over any implementation and is never nested twice.

Statements, transactions, batches (shared semantics)

  • One statement per call; bind with ? and an argument list; values are int, num, String, Uint8List or null (bool, DateTime, List, Map are not supported). Results are read-only maps.
  • db.transaction((txn) async {...}) commits when the callback returns and rolls back (and rethrows) when it throws. Use only txn inside. Callbacks onCreate / onUpgrade / onDowngrade are already in a transaction.
  • db.batch() collects operations and commit() runs them in one call in a transaction (noResult: true, continueOnError: true); a batch created from a Transaction is committed with it.
  • queryIterate / rawQueryIterate (or queryCursor + moveNext() + close()) stream large results with a bufferSize (default 100).
  • DatabaseException helpers: isNoSuchTableError, isSyntaxError, isUniqueConstraintError, isNotNullConstraintError, isDuplicateColumnError, isOpenFailedError, isDatabaseClosedError, isReadOnlyError, getResultCode().

Examples

Shared repository, used from Flutter and from a Dart test

// package my_data (depends on sqflite_common only)
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common/utils/utils.dart';

class TodoRepository {
  TodoRepository({required this.factory, required this.path});

  final DatabaseFactory factory;
  final String path;
  Future<Database>? _db;

  Future<Database> get db => _db ??= factory.openDatabase(
        path,
        options: OpenDatabaseOptions(
          version: 1,
          onConfigure: (db) => db.execute('PRAGMA foreign_keys = ON'),
          onCreate: (db, _) => db.execute(
            'CREATE TABLE Todo (id INTEGER PRIMARY KEY, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0)',
          ),
        ),
      );

  Future<int> add(String title) async =>
      (await db).insert('Todo', {'title': title});

  Future<int> count(DatabaseExecutor executor) async =>
      firstIntValue(await executor.query('Todo', columns: [sqlCountColumn])) ?? 0;

  Future<void> markAllDone() async {
    final database = await db;
    await database.transaction((txn) async {
      final pending = await count(txn);
      if (pending > 0) {
        await txn.update('Todo', {'done': 1});
      }
    });
  }

  Future<void> close() async {
    await (await db).close();
    _db = null;
  }
}
// test/todo_repository_test.dart (dev_dependency: sqflite_common_ffi)
// sqflite_ffi.dart re-exports the sqflite_common API (inMemoryDatabasePath...).
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:test/test.dart';

import 'package:my_data/todo_repository.dart';

void main() {
  setUpAll(sqfliteFfiInit);

  test('count and markAllDone', () async {
    final repo = TodoRepository(
      factory: databaseFactoryFfi,
      path: inMemoryDatabasePath,
    );
    await repo.add('a');
    expect(await repo.count(await repo.db), 1);
    await repo.markAllDone();
    expect(await (await repo.db).query('Todo', where: 'done = 1'), hasLength(1));
    await repo.close();
  });
}

In the Flutter app: TodoRepository(factory: databaseFactory, path: join(await getDatabasesPath(), 'todo.db')) with package:sqflite/sqflite.dart.

Using the global factory in an application

import 'package:sqflite_common/sqflite.dart';

/// The app (not a library) sets databaseFactory first, e.g.
/// `databaseFactory = databaseFactoryFfi;` from sqflite_common_ffi.
Future<int> countRows(String table) async {
  final db = await openDatabase(inMemoryDatabasePath);
  try {
    await db.execute('CREATE TABLE $table (id INTEGER PRIMARY KEY)');
    final rows = await db.rawQuery('SELECT COUNT(*) FROM $table');
    return rows.first.values.first as int;
  } finally {
    await db.close();
  }
}

Sandboxed factory for tests or per-user data

import 'package:sqflite_common/sqlite_api.dart';

Future<Database> openUserDb(DatabaseFactory factory, String userRoot) async {
  final userFactory = factory.sandbox(path: userRoot);
  // Relative to userRoot; '/etc/x.db' would throw ArgumentError.
  return userFactory.openDatabase('cache.db');
}

Logging every statement of any factory

import 'package:sqflite_common/sqflite_logger.dart';
import 'package:sqflite_common/sqlite_api.dart';

// ignore: experimental_member_use
DatabaseFactory withLogs(DatabaseFactory factory) => SqfliteDatabaseFactoryLogger(
      factory,
      options: SqfliteLoggerOptions(
        type: SqfliteDatabaseFactoryLoggerType.all,
        log: (event) {
          if (event is SqfliteLoggerSqlEvent) {
            print('${event.type.name}: ${event.sql} ${event.arguments ?? ''}'
                '${event.error != null ? ' error: ${event.error}' : ''}');
          } else if (event is SqfliteLoggerBatchEvent) {
            for (final op in event.operations) {
              print('batch ${op.type.name}: ${op.sql} ${op.arguments ?? ''}');
            }
          } else {
            event.dump();
          }
        },
      ),
    );

Prepared commands and iteration

import 'package:sqflite_common/sqlite_api.dart';

final _byDone = SqfliteSqlCommand.query('Todo', where: 'done = ?', whereArgs: [0]);
final _insert = SqfliteSqlCommand.insert('Todo', {'title': 'x', 'done': 0});

Future<List<String>> pendingTitles(DatabaseExecutor executor) async {
  final titles = <String>[];
  await _byDone.iterate(executor, bufferSize: 50, onRow: (row) {
    titles.add(row['title'] as String);
    return true;
  });
  return titles;
}

Future<int> insertOne(DatabaseExecutor executor) => _insert.insert(executor);

Future<void> walk(DatabaseExecutor executor) => executor.queryIterate(
      'Todo',
      orderBy: 'id',
      onRow: (row) async {
        print(row);
        return row['id'] != 100; // stop at id 100
      },
    );

Reading the schema version and enabling WAL

import 'package:sqflite_common/sqlite_api.dart';

Future<Database> openWithWal(DatabaseFactory factory, String path) =>
    factory.openDatabase(
      path,
      options: OpenDatabaseOptions(
        version: 2,
        onConfigure: (db) async {
          await db.setJournalMode('WAL');
          if (await db.getVersion() == 0) {
            // New file: settings that must precede table creation.
            await db.execute('PRAGMA auto_vacuum = 2');
          }
        },
        onCreate: (db, v) => db.execute('CREATE TABLE T (id INTEGER PRIMARY KEY)'),
        onUpgrade: (db, old, _) async {
          if (old < 2) await db.execute('ALTER TABLE T ADD name TEXT');
        },
        onDowngrade: onDatabaseDowngradeDelete,
      ),
    );

Common mistakes

  • Depending on package:sqflite in a package that also runs on the Dart VM or in dart test; depend on sqflite_common and inject the factory.
  • Importing package:sqflite_common/sqflite.dart in a library only for the types; use sqlite_api.dart so the library does not read the global.
  • Calling openDatabase(...) (global) before the app set databaseFactory (StateError: databaseFactory not initialized).
  • Assigning a hand-written DatabaseFactory implementation to databaseFactory: the setter only accepts sqflite implementations. Wrap with SqfliteDatabaseFactoryLogger or use sandbox() instead.
  • Using db inside db.transaction((txn) ...), or db.transaction() inside onCreate / onUpgrade.
  • Relying on getDatabasesPath() from a non-plugin factory for real data; pass an explicit path.

More

Implementation packages: sqflite (Flutter, Android/iOS/macOS), sqflite_common_ffi (desktop, VM, tests), sqflite_common_ffi_web (web); each ships its own skills. Docs in the repository: doc/sqflite_iterate.md, doc/sqflite_sql_command.md, doc/sqflite_logger.md, doc/method_call_protocol.md.

Version History

  • aaabf90 Current 2026-09-22 03:38

Same Skill Collection

packages/sqflite_common_ffi_async_test/skills/sqflite-common-ffi-async-test-suite/SKILL.md
packages/sqflite_common_ffi_async/skills/sqflite-common-ffi-async-factory/SKILL.md
sqflite_android/skills/sqflite-android-setup/SKILL.md
sqflite_common_ffi/skills/sqflite-common-ffi-testing/SKILL.md
packages_flutter/sqflite_ffi/skills/sqflite-ffi-flutter/SKILL.md
packages_web/sqflite_common_ffi_web_test/skills/sqflite-common-ffi-web-test-suite/SKILL.md
packages_web/sqflite_common_ffi_web/skills/sqflite-common-ffi-web-options/SKILL.md
packages_web/sqflite_common_ffi_web/skills/sqflite-common-ffi-web-setup/SKILL.md
sqflite_common_ffi/skills/sqflite-common-ffi-desktop/SKILL.md
sqflite_common_test/skills/sqflite-common-test-suite/SKILL.md
sqflite_darwin/skills/sqflite-darwin-setup/SKILL.md
sqflite_platform_interface/skills/sqflite-platform-interface-implementers/SKILL.md
sqflite/skills/sqflite-crud-and-transactions/SKILL.md
sqflite/skills/sqflite-open-database/SKILL.md
sqflite/skills/sqflite-testing-and-platforms/SKILL.md

Metadata

Files
0
Version
aaabf90
Hash
81bbab59
Indexed
2026-09-22 03:38

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-22 09:59
浙ICP备14020137号-1