Skip to main content
This guide is currently specific to the Dart/Flutter SDK. We may expand it to cover other SDKs in the future.
Our demo apps for Flutter are intentionally kept simple to focus on demonstrating PowerSync APIs. Instead of using heavy state management solutions, they use simple global fields to make the PowerSync database accessible to widgets. When adopting PowerSync in your own app, you might want a more sophisticated approach for state management. This guide explains how PowerSync’s Dart/Flutter SDK integrates with popular state management packages.
Adopting PowerSync can actually simplify your app architecture by using a local SQLite database as the single source of truth for all data. For a general discussion on how PowerSync fits into modern app architecture, see this blog post.
PowerSync exposes database queries with the standard Future and Stream classes from dart:async. Given how widely used these are in the Dart ecosystem, PowerSync works well with all popular approaches for state management, such as:
  1. Providers with package:provider: Create your database as a Provider and expose watched queries to child widgets with StreamProvider! The provider for databases should close() the database in dispose.
  2. Providers with package:riverpod: We mention relevant snippets below.
  3. Dependency injection with package:get_it: PowerSync databases can be registered with registerSingletonAsync. Again, make sure to close() the database in the dispose callback.
  4. The BLoC pattern with the bloc package: You can easily listen to watched queries in Cubits (although, if you find your Blocs and Cubits becoming trivial wrappers around database streams, consider just watch()ing database queries in widgets directly. That doesn’t make your app less testable!). To simplify state management, avoid the use of hydrated blocs and cubits for state that depends on database queries. With PowerSync, regular data is already available locally and doesn’t need a second local cache.

Riverpod

We have a complete example using PowerSync with modern Flutter libraries like Riverpod, Drift, and auto_route.
A good way to open PowerSync databases with Riverpod is to use an async provider. You can manage your connect and disconnect calls there, for instance by listening to authentication state:

Querying Data

To expose auto-updating query results, use a StreamProvider that reads from the database:

Waiting for Sync

If you were awaiting waitForFirstSync before, you can keep doing that:
Alternatively, you can expose the sync status as a provider and use that to determine whether the sync has completed:

Attachment Queue

If you’re using the attachment queue helper to sync media assets, you can also wrap that in a provider:
Reading and awaiting this provider can then be used to show attachments: