PowerSync SDK for Dart/Flutter

PowerSync is a service and set of SDKs that keeps PostgreSQL databases in sync with on-device SQLite databases.

SDK Features

  • Real-time streaming of changes.
  • Direct access to the SQLite database - use SQL on the client and server.
  • Operations are asynchronous by default - does not block the UI.
  • Supports one write and many reads concurrently.
  • No need for client-side database migrations - these are handled automatically.
  • Subscribe to queries for live updates.


For complete app examples, see our example app gallery

For examples of some common patterns, see our example snippets

Getting started

You'll need to create a PowerSync account and set up a PowerSync instance. You can do this at https://www.powersync.com/.

Install the package

flutter pub add powersync

Implement a backend connector and initialize the PowerSync database

import 'package:powersync/powersync.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';

// Define the schema for the local SQLite database.
// You can automatically generate this schema based on your sync rules:
// In the PowerSync dashboard, right-click on your PowerSync instance and then click "Generate client-side schema"
const schema = Schema([
  Table('customers', [Column.text('name'), Column.text('email')])

late PowerSyncDatabase db;

// You must implement a backend connector to define how PowerSync communicates with your backend.
class MyBackendConnector extends PowerSyncBackendConnector {
  PowerSyncDatabase db;

  Future<PowerSyncCredentials?> fetchCredentials() async {
    // implement fetchCredentials to obtain a JWT from your authentication service
    // see https://docs.powersync.com/usage/installation/authentication-setup
  Future<void> uploadData(PowerSyncDatabase database) async {
    // Implement uploadData to send local changes to your backend service
    // You can omit this method if you only want to sync data from the server to the client
    // see https://docs.powersync.com/usage/installation/upload-data

openDatabase() async {
  final dir = await getApplicationSupportDirectory();
  final path = join(dir.path, 'powersync-dart.db');

  // Setup the database.
  db = PowerSyncDatabase(schema: schema, path: path);
  await db.initialize();

  // Connect to backend
  db.connect(connector: MyBackendConnector(db));

Subscribe to changes in data

  // you can watch any SQL query
  stream: return db.watch('SELECT * FROM customers order by id asc'),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      // TODO: implement your own UI here based on the result set
      return ...;
    } else {
      return const Center(child: CircularProgressIndicator());

Insert, update, and delete data in the SQLite database as you would normally

  onPressed: () async {
    await db.execute(
      'INSERT INTO customers(id, name, email) VALUES(uuid(), ?, ?)',
      ['Fred', 'fred@example.org'],
  tooltip: '+',
  child: const Icon(Icons.add),

Send changes in local data to your backend service

// Implement the uploadData method in your backend connector
Future<void> uploadData(PowerSyncDatabase database) async {
  final batch = await database.getCrudBatch();
  if (batch == null) return;
  for (var op in batch.crud) {
    switch (op.op) {
      case UpdateType.put:
        // Send the data to your backend service
        // replace `_myApi` with your own API client or service
        await _myApi.put(op.table, op.opData!);
        // TODO: implement the other operations (patch, delete)
  await batch.complete();


You can enable logging to see what's happening under the hood or to debug connection/authentication/sync issues.

Logger.root.level = Level.INFO;
Logger.root.onRecord.listen((record) {
  if (kDebugMode) {
    print('[${record.loggerName}] ${record.level.name}: ${record.time}: ${record.message}');

    if (record.error != null) {
    if (record.stackTrace != null) {


PowerSync Dart SDK.
Re-exports sqlite3 to expose sqlite3 without adding it as a direct dependency.
Re-exports sqlite_async to expose sqlite_async without adding it as a direct dependency.