Skip to content

Improvements to code examples #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 2, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 187 additions & 48 deletions client-sdk-references/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@
* [Configured your backend database](/installation/database-setup) and connected it to your PowerSync instance.
* [Installed](/client-sdk-references/flutter#installation) the PowerSync Flutter SDK.

<Note>
For this reference document, we assume that you have created a Flutter project and have the following directory structure:

```plaintext
lib/
├── models/
├── schema.dart
└── todolist.dart
├── powersync/
├── my_backend_connector.dart
└── powersync.dart
├── widgets/
├── lists_widget.dart
├── todos_widget.dart
├── main.dart
```
</Note>

### 1\. Define the Schema

The first step is defining the schema for the local SQLite database. This will be provided as a `schema` parameter to the [PowerSyncDatabase](https://pub.dev/documentation/powersync/latest/powersync/PowerSyncDatabase/PowerSyncDatabase.html) constructor.
Expand All @@ -58,12 +76,11 @@
Similar functionality exists in the [CLI](/usage/tools/cli).

</Info>
The types available are `text`, `integer` and `real`. These should map directly to the values produced by the [Sync Rules](/usage/sync-rules). If a value doesn't match, it is cast automatically. For details on how Postgres types are mapped to the types below, see the section on [Types](/usage/sync-rules/types) in the _Sync Rules_ documentation.

Check warning on line 79 in client-sdk-references/flutter.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

client-sdk-references/flutter.mdx#L79

Did you really mean '_Sync'?

Check warning on line 79 in client-sdk-references/flutter.mdx

View check run for this annotation

Mintlify / Mintlify Validation - vale-spellcheck

client-sdk-references/flutter.mdx#L79

Did you really mean 'Rules_'?

**Example**:

```dart
// lib/models/schema.dart
```dart lib/models/schema.dart
import 'package:powersync/powersync.dart';

const schema = Schema(([
Expand Down Expand Up @@ -101,10 +118,12 @@

**Example**:

```dart
import 'package:powersync/powersync.dart';
import 'package:path_provider/path_provider.dart';
```dart lib/powersync/powersync.dart
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:powersync/powersync.dart';
import '../main.dart';
import '../models/schema.dart';

openDatabase() async {
final dir = await getApplicationSupportDirectory();
Expand All @@ -119,9 +138,49 @@

Once you've instantiated your PowerSync database, you will need to call the [connect()](https://pub.dev/documentation/powersync/latest/powersync/PowerSyncDatabase/connect.html) method to activate it. This method requires the backend connector that will be created in the next step.

```dart
// Uses the backend connector that will be created in the next step
db.connect(connector: MyBackendConnector(db));
```dart lib/main.dart {35}
import 'package:flutter/material.dart';
import 'package:powersync/powersync.dart';

import 'powersync/powersync.dart';

late PowerSyncDatabase db;

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

await openDatabase();
runApp(const DemoApp());
}

class DemoApp extends StatefulWidget {
const DemoApp({super.key});

@override
State<DemoApp> createState() => _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: // TODO: Implement your own UI here.
// You could listen for authentication state changes to connect or disconnect from PowerSync
StreamBuilder(
stream: // TODO: some stream,
builder: (ctx, snapshot) {,
// TODO: implement your own condition here
if ( ... ) {
// Uses the backend connector that will be created in the next step
db.connect(connector: MyBackendConnector());
// TODO: implement your own UI here
}
},
)
);
}
}
```

### 3\. Integrate with your Backend
Expand All @@ -141,12 +200,8 @@

**Example**:

```dart
```dart lib/powersync/my_backend_connector.dart
import 'package:powersync/powersync.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';

late PowerSyncDatabase db;

class MyBackendConnector extends PowerSyncBackendConnector {
PowerSyncDatabase db;
Expand All @@ -161,18 +216,41 @@

// See example implementation here: https://pub.dev/documentation/powersync/latest/powersync/DevConnector/fetchCredentials.html

return {
endpoint: '[Your PowerSync instance URL or self-hosted endpoint]',
// Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly
token: 'An authentication token'
};
return PowerSyncCredentials(
endpoint: 'https://xxxxxx.powersync.journeyapps.com',
// Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly
token: 'An authentication token'
);
}

// 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 example implementation here: https://docs.powersync.com/client-sdk-references/flutter#3-integrate-with-your-backend
@override
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
// This function is called whenever there is data to upload, whether the
// device is online or offline.
// If this call throws an error, it is retried periodically.

final transaction = await database.getNextCrudTransaction();
if (transaction == null) {
return;
}

// See example implementation here: https://docs.powersync.com/client-sdk-references/flutter#3-integrate-with-your-backend
// The data that needs to be changed in the remote db
for (var op in transaction.crud) {
switch (op.op) {
case UpdateType.put:
// TODO: Instruct your backend API to CREATE a record
case UpdateType.patch:
// TODO: Instruct your backend API to PATCH a record
case UpdateType.delete:
//TODO: Instruct your backend API to DELETE a record
}
}

// Completes the transaction and moves onto the next one
await transaction.complete();
}
}

Expand All @@ -189,12 +267,43 @@
* [PowerSyncDatabase.watch](/client-sdk-references/flutter#watching-queries-powersync.watch) \- execute a read query every time source tables are modified.
* [PowerSyncDatabase.execute](/client-sdk-references/flutter#mutations-powersync.execute) \- execute a write (INSERT/UPDATE/DELETE) query.

For the following examples, we will define a `TodoList` model class that represents a List of todos.

```dart lib/models/todolist.dart
/// This is a simple model class representing a TodoList
class TodoList {
final int id;
final String name;
final DateTime createdAt;
final DateTime updatedAt;

TodoList({
required this.id,
required this.name,
required this.createdAt,
required this.updatedAt,
});

factory TodoList.fromRow(Map<String, dynamic> row) {
return TodoList(
id: row['id'],
name: row['name'],
createdAt: DateTime.parse(row['created_at']),
updatedAt: DateTime.parse(row['updated_at']),
);
}
}
```

### Fetching a Single Item

The [get](https://pub.dev/documentation/powersync/latest/sqlite_async/SqliteQueries/get.html) method executes a read-only (SELECT) query and returns a single result. It throws an exception if no result is found. Use [getOptional](https://pub.dev/documentation/powersync/latest/sqlite_async/SqliteQueries/getOptional.html) to return a single optional result (returns `null` if no result is found).

```dart
// Find a list item by ID
The following is an example of selecting a list item by ID
```dart lib/widgets/lists_widget.dart
import '../main.dart';
import '../models/todolist.dart';

Future<TodoList> find(id) async {
final result = await db.get('SELECT * FROM lists WHERE id = ?', [id]);
return TodoList.fromRow(result);
Expand All @@ -205,48 +314,78 @@

The [getAll](https://pub.dev/documentation/powersync/latest/sqlite_async/SqliteQueries/getAll.html) method returns a set of rows from a table.

```dart
/// Get all list IDs
```dart lib/widgets/lists_widget.dart
import 'package:powersync/sqlite3.dart';
import '../main.dart';

Future<List<String>> getLists() async {
ResultSet results = await db.getAll('SELECT id FROM lists WHERE id IS NOT NULL');
List<String> ids = results.map((row) => row['id'] as String).toList();
return ids;
}
```

### Watching Queries (PowerSync.watch)

The [watch](https://pub.dev/documentation/powersync/latest/sqlite_async/SqliteQueries/watch.html) method executes a read query whenever a change to a dependent table is made.

```dart
StreamBuilder(
// You can watch any SQL query
stream: 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());
}
},
)
```dart lib/widgets/todos_widget.dart {13-17}
import 'package:flutter/material.dart';
import '../main.dart';
import '../models/todolist.dart';

// Example Todos widget
class TodosWidget extends StatelessWidget {
const TodosWidget({super.key});

@override
Widget build(BuildContext context) {
return StreamBuilder(
// You can watch any SQL query
stream: db
.watch('SELECT * FROM lists ORDER BY created_at, id')
.map((results) {
return results.map(TodoList.fromRow).toList(growable: false);
}),
builder: (context, snapshot) {
if (snapshot.hasData) {
// TODO: implement your own UI here based on the result set
return ...;
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
}
```

### Mutations (PowerSync.execute)

The [execute](https://pub.dev/documentation/powersync/latest/powersync/PowerSyncDatabase/execute.html) method can be used for executing single SQLite write statements.

```dart
FloatingActionButton(
onPressed: () async {
await db.execute(
'INSERT INTO customers(id, name, email) VALUES(uuid(), ?, ?)',
['Fred', 'fred@example.org'],
```dart lib/widgets/todos_widget.dart {12-15}
import 'package:flutter/material.dart';
import '../main.dart';

// Example Todos widget
class TodosWidget extends StatelessWidget {
const TodosWidget({super.key});

@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () async {
await db.execute(
'INSERT INTO lists(id, created_at, name, owner_id) VALUES(uuid(), datetime(), ?, ?)',
['name', '123'],
);
},
tooltip: '+',
child: const Icon(Icons.add),
);
},
tooltip: '+',
child: const Icon(Icons.add),
);
}
}
```

## Additional Usage Examples
Expand Down
Loading