|
11 | 11 | * No need for client-side database migrations - these are handled automatically. |
12 | 12 | * Subscribe to queries for live updates. |
13 | 13 |
|
| 14 | +## Examples |
| 15 | + |
| 16 | +For a number of complete app examples, see our [example app gallery](https://docs.powersync.com/resources/demo-apps-example-projects#flutter) |
| 17 | + |
14 | 18 | ## Getting started |
15 | 19 |
|
| 20 | +You'll need to create a PowerSync account and set up a PowerSync instance. You can do this at [https://www.powersync.com/](https://www.powersync.com/). |
| 21 | + |
| 22 | +### Install the package |
| 23 | + |
| 24 | +`flutter pub add powersync` |
| 25 | + |
| 26 | +### Implement a backend connector and initialize the Powersync database |
| 27 | + |
16 | 28 | ```dart |
17 | 29 | import 'package:powersync/powersync.dart'; |
18 | 30 | import 'package:path_provider/path_provider.dart'; |
19 | 31 | import 'package:path/path.dart'; |
20 | 32 |
|
| 33 | +// Define the schema for the local SQLite database. |
| 34 | +// You can automatically generate this schema based on your sync rules: |
| 35 | +// In the Powersync dashboard, right-click on your PowerSync instance and then click "Generate client-side schema" |
21 | 36 | const schema = Schema([ |
22 | 37 | Table('customers', [Column.text('name'), Column.text('email')]) |
23 | 38 | ]); |
24 | 39 |
|
25 | 40 | late PowerSyncDatabase db; |
26 | 41 |
|
27 | | -// Setup connector to backend if you would like to sync data. |
28 | | -class BackendConnector extends PowerSyncBackendConnector { |
| 42 | +// You must implement a backend connector to define how Powersync communicates with your backend. |
| 43 | +class MyBackendConnector extends PowerSyncBackendConnector { |
29 | 44 | PowerSyncDatabase db; |
30 | 45 |
|
31 | | - BackendConnector(this.db); |
| 46 | + MyBackendConnector(this.db); |
32 | 47 | @override |
33 | 48 | Future<PowerSyncCredentials?> fetchCredentials() async { |
34 | | - // implement fetchCredentials |
| 49 | + // implement fetchCredentials to obtain a JWT from your authentication service |
| 50 | + // see https://docs.powersync.com/usage/installation/authentication-setup |
35 | 51 | } |
36 | 52 | @override |
37 | 53 | Future<void> uploadData(PowerSyncDatabase database) async { |
38 | | - // implement uploadData |
| 54 | + // Implement uploadData to send local changes to your backend service |
| 55 | + // You can omit this method if you only want to sync data from the server to the client |
| 56 | + // see https://docs.powersync.com/usage/installation/upload-data |
39 | 57 | } |
40 | 58 | } |
41 | 59 |
|
42 | 60 | openDatabase() async { |
43 | 61 | final dir = await getApplicationSupportDirectory(); |
44 | 62 | final path = join(dir.path, 'powersync-dart.db'); |
| 63 | +
|
45 | 64 | // Setup the database. |
46 | 65 | db = PowerSyncDatabase(schema: schema, path: path); |
47 | 66 | await db.initialize(); |
48 | 67 |
|
49 | | - // Run local statements. |
50 | | - await db.execute( |
| 68 | + // Connect to backend |
| 69 | + db.connect(connector: MyBackendConnector(db)); |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +### Subscribe to changes in data |
| 74 | + |
| 75 | +```dart |
| 76 | +StreamBuilder( |
| 77 | + // you can watch any SQL query |
| 78 | + stream: return db.watch('SELECT * FROM customers order by id asc'), |
| 79 | + builder: (context, snapshot) { |
| 80 | + if (snapshot.hasData) { |
| 81 | + // TODO: implement your own UI here based on the result set |
| 82 | + return ...; |
| 83 | + } else { |
| 84 | + return const Center(child: CircularProgressIndicator()); |
| 85 | + } |
| 86 | + }, |
| 87 | +) |
| 88 | +``` |
| 89 | + |
| 90 | +### Insert, update, and delete data in the SQLite database as you would normally |
| 91 | + |
| 92 | +```dart |
| 93 | +FloatingActionButton( |
| 94 | + onPressed: () async { |
| 95 | + await db.execute( |
51 | 96 | 'INSERT INTO customers(id, name, email) VALUES(uuid(), ?, ?)', |
52 | | - ['Fred', 'fred@example.org']); |
| 97 | + ['Fred', 'fred@example.org'], |
| 98 | + ); |
| 99 | + }, |
| 100 | + tooltip: '+', |
| 101 | + child: const Icon(Icons.add), |
| 102 | +); |
| 103 | +``` |
53 | 104 |
|
| 105 | +### Send changes in local data to your backend service |
54 | 106 |
|
55 | | - // Connect to backend |
56 | | - db.connect(connector: BackendConnector(db)); |
| 107 | +```dart |
| 108 | +// Implement the uploadData method in your backend connector |
| 109 | +@override |
| 110 | +Future<void> uploadData(PowerSyncDatabase database) async { |
| 111 | + final batch = await database.getCrudBatch(); |
| 112 | + if (batch == null) return; |
| 113 | + for (var op in batch.crud) { |
| 114 | + switch (op.op) { |
| 115 | + case UpdateType.put: |
| 116 | + // Send the data to your backend service |
| 117 | + await _myApi.put(op.table, op.opData!); |
| 118 | + break; |
| 119 | + default: |
| 120 | + // TODO: implement the other operations (patch, delete) |
| 121 | + break; |
| 122 | + } |
| 123 | + } |
| 124 | + await batch.complete(); |
57 | 125 | } |
58 | 126 | ``` |
| 127 | + |
| 128 | +### Logging |
| 129 | + |
| 130 | +You can enable logging to see what's happening under the hood |
| 131 | +or to debug connection/authentication/sync issues. |
| 132 | + |
| 133 | +```dart |
| 134 | +Logger.root.level = Level.INFO; |
| 135 | +Logger.root.onRecord.listen((record) { |
| 136 | + if (kDebugMode) { |
| 137 | + print('[${record.loggerName}] ${record.level.name}: ${record.time}: ${record.message}'); |
| 138 | +
|
| 139 | + if (record.error != null) { |
| 140 | + print(record.error); |
| 141 | + } |
| 142 | + if (record.stackTrace != null) { |
| 143 | + print(record.stackTrace); |
| 144 | + } |
| 145 | + } |
| 146 | +}); |
| 147 | +``` |
| 148 | + |
0 commit comments