');
-```
+import 'package:appwrite/enums.dart' as enums;
-> If using a self-hosted instance, you will also need to set your Appwrite endpoint using the `setEndpoint` method. Before starting to send any API calls to your new Appwrite instance, make sure your Android or iOS emulators has network access to the Appwrite server hostname or IP address.
-> When trying to connect to a local Appwrite instance from an emulator or a mobile device, localhost is the hostname for the device or emulator and not your machine. You should replace localhost with your machine's private IP as the Appwrite endpoint's hostname (e.g. 192.168.1.100). You can also use a service like [ngrok](https://ngrok.com/) to proxy the Appwrite API.
+await account.createOAuth2Session(provider: enums.OAuthProvider.github);
+```
-### Make Your First Request
+### Initialize the client
-Once your SDK object is set, access any of the Appwrite services and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the [API References](https://appwrite.io/docs) section.
+Initialize the client with your project's endpoint and ID, both found on your project's **Settings** page in the Console.
```dart
-Account account = Account(client);
+import 'package:appwrite/appwrite.dart';
-User user = await account.create(
- userId: ID.unique(),
- email: 'email@example.com',
- password: 'password',
- name: "Walter O'Brien",
-);
+Client client = Client()
+ .setEndpoint('https://.cloud.appwrite.io/v1')
+ .setProject('');
```
-### Full Example
+> If you self-host Appwrite, use your instance's endpoint instead. On an emulator or a device, `localhost` is the device itself, not your computer: use your computer's private IP address (for example `http://192.168.1.100/v1`) or a tunnel such as [ngrok](https://ngrok.com/). Call `setSelfSigned()` only in development, when the instance uses a self-signed certificate.
+
+### Make a request
+
+Every service takes the client in its constructor, and every method takes named parameters and returns a typed model from `package:appwrite/models.dart`.
```dart
-Client client = Client().setProject('');
+import 'package:appwrite/appwrite.dart';
+import 'package:appwrite/models.dart' as models;
Account account = Account(client);
-User user = await account.create(
+models.User user = await account.create(
userId: ID.unique(),
email: 'email@example.com',
password: 'password',
- name: "Walter O'Brien"
+ name: "Walter O'Brien",
+);
+
+models.Session session = await account.createEmailPasswordSession(
+ email: 'email@example.com',
+ password: 'password',
);
```
-### Error Handling
-The Appwrite Flutter SDK raises `AppwriteException` object with `message`, `type`, `code` and `response` properties. You can handle any errors by catching `AppwriteException` and present the `message` to the user or handle it yourself based on the provided error information. Below is an example.
+### Error handling
+
+The SDK throws an `AppwriteException` with `message`, `code`, `type` and `response` properties. Catch it to present the `message` to the user or to handle the error based on its `type`.
```dart
try {
- User user = await account.create(...);
-} on AppwriteException catch(e) {
- // Handle the exception
+ models.User user = await account.get();
+} on AppwriteException catch (e) {
+ print('${e.code} ${e.type}: ${e.message}');
}
```
### Learn more
-You can use the following resources to learn more and get help
-- 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-flutter)
-- 📜 [Appwrite Docs](https://appwrite.io/docs)
-- 💬 [Discord Community](https://appwrite.io/discord)
-- 🚂 [Appwrite Flutter Playground](https://github.com/appwrite/playground-for-flutter)
+- [Flutter quick start](https://appwrite.io/docs/quick-starts/flutter)
+- [Client Flutter API reference](https://appwrite.io/docs/references/cloud/client-flutter/account)
+- [Appwrite docs](https://appwrite.io/docs)
+- [Discord community](https://appwrite.io/discord)
+- [Appwrite Flutter playground](https://github.com/appwrite/playground-for-flutter)
+
+## Push
+
+`Push` delivers messages from Appwrite to your app over a persistent connection, without FCM or APNs. Subscribe to one or more topics and handle each message in a callback:
+
+```dart
+final push = Push(client);
+
+final subscription = await push.subscribe('news', (message) {
+ print('${message.topic}: ${message.data}');
+});
+```
+
+Keep `subscription` while you want messages, and call `subscription.unsubscribe()` when you stop listening, for example in your widget's `dispose()`.
+
+### Background delivery on Android
+
+A subscription with `background: true` keeps delivering after the app is backgrounded, killed or the device restarts, until it is unsubscribed or `push.close()` is called (do this on sign-out). The plugin saves the subscription, and a scheduled job and alarm wake the app every 15 to 60 seconds to reconnect; the broker replays what was sent in between (`retry: true`). While the app is not on screen, each message is posted as a notification that opens the app. It reconnects with the credential saved at subscribe time, so use a session rather than a short-lived JWT.
+
+On Android 13 and later, the first background subscription asks the user for the `POST_NOTIFICATIONS` runtime permission. If they decline, the subscription still delivers to your callback but posts no notification. Notifications show the title, body and image sent with `createPush`, falling back to the subscription's `title` when only a body is sent. A push with no title or body, such as a data-only push, posts no notification and reaches your callback only. Messages that are not a JSON object show the subscription's `title` with the raw payload as body.
+
+Notifications are posted while the app is backgrounded or closed. While it is on screen your callback shows the message, so none is posted unless the subscription passes `notifyInForeground: true`.
+
+```dart
+final subscription = await push.subscribe(
+ 'news',
+ (message) => print(message.data),
+ background: true,
+ title: 'News',
+);
+
+// Optional: immediate delivery even after a kill and during Doze, with a quiet ongoing
+// notification (call while the app is in the foreground).
+await push.setForeground(true);
+```
+
+Foreground mode runs a `remoteMessaging` foreground service, which Google Play asks apps to declare in the Play Console. Apps that never enable it can remove the service from their merged manifest with `tools:node="remove"` on `io.appwrite.flutter.services.PushService` and `android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING`.
+
+Set the notification icon with `` in your ``; without it a generic icon is used.
+
+#### Delivery while the app is closed
+
+Messages sent while the app is closed arrive at the next scheduled wake-up. While the device is awake that is about every 15 seconds, or about every 60 seconds once exact alarms are allowed; without exact alarms the wake-ups are inexact, so battery saver can defer them further. In Doze (screen off and idle for a while) Android limits background alarms, exact ones included, to about one every nine minutes, so a closed app can take several minutes to receive a message: allowing exact alarms makes wake-ups punctual, it does not lift Doze. For immediate delivery, also in Doze, use `push.setForeground(true)` (see above).
+
+The SDK uses exact alarms on its own whenever the app may schedule them. To allow it:
+
+1. Declare the permissions in your app's `AndroidManifest.xml`. Both are optional and subject to Google Play policy: `SCHEDULE_EXACT_ALARM` needs a declaration in the Play Console, and `USE_EXACT_ALARM` is reserved for alarm, clock and calendar apps (the SDK does not use it).
+
+ ```xml
+
+
+ ```
+
+2. On Android 13 and later the user has to allow exact alarms, under Settings > Apps > Special app access > Alarms & reminders. Android 12 grants a declared `SCHEDULE_EXACT_ALARM` automatically, and older versions need nothing. Check with `await push.backgroundStatus()`: when `bestEffort` is true, explain why to the user, then from a user action open that screen with `push.requestExactAlarms()`, or ask for the battery-optimisation exemption with `push.requestIgnoreBatteryOptimizations()`. Both return false when there is nothing to ask, including when the permission is not declared. The SDK never opens these screens on its own.
+
+If `backgroundRestricted` is true, the user restricted the app's background usage and messages can wait until the app is opened. Exact alarms don't lift it, and neither does foreground mode: Android stops a restricted app's foreground service once it leaves the screen. Ask the user to choose Unrestricted in the app's battery settings (Settings > Apps > your app > Battery); granting the battery optimisation exemption also lets the SDK's alarms and jobs run, and `bestEffort` then reflects only the exact-alarm state. It is always false below Android 9.
+
+If the user force-stops the app (Settings > Force stop, and on some devices swiping it away from recents), Android cancels its alarms and jobs: nothing is delivered until the app is opened again, and the broker then replays what was sent meanwhile.
+
+### Opening a tapped notification
+
+Read the `data` sent with `createPush` when the user taps a background notification:
+
+```dart
+// The tap that launched the app (reported once, so call it at startup).
+final opened = await push.getInitialNotification();
+if (opened != null) openSale(opened.data['saleId']);
+
+// Taps while the app is running, including in the background.
+final stop = push.onNotificationOpened((opened) => openSale(opened.data['saleId']));
+```
+
+On Android the SDK's native plugin reports the taps, on iOS `flutter_local_notifications`, and on the web the notification's click while the page is open.
## Contribution
-This library is auto-generated by Appwrite custom [SDK Generator](https://github.com/appwrite/sdk-generator). To learn more about how you can help us improve this SDK, please check the [contribution guide](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md) before sending a pull-request.
+This library is auto-generated by Appwrite custom [SDK Generator](https://github.com/appwrite/sdk-generator). To learn more about how you can help us improve this SDK, please check the [contribution guide](https://github.com/appwrite/sdk-generator/blob/main/CONTRIBUTING.md) before sending a pull-request.
## License
-Please see the [BSD-3-Clause license](https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE) file for more information.
\ No newline at end of file
+Please see the [BSD-3-Clause license](https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE) file for more information.
diff --git a/example/README.md b/example/README.md
index 3735ef38..a671c813 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,63 +1,98 @@
# Examples
-Init your Appwrite client:
+Initialize the client:
```dart
-Client client = Client();
+import 'package:appwrite/appwrite.dart';
+import 'package:appwrite/models.dart' as models;
-client
- .setEndpoint('https://localhost:9521/v1') // Your Appwrite Endpoint
- .setProject('5e8cf4f46b5e8') // Your project ID
- .setSelfSigned() // Remove in production
-;
+Client client = Client()
+ .setEndpoint('https://.cloud.appwrite.io/v1')
+ .setProject('');
```
-Create a new user and session:
+Create a user and sign in:
```dart
Account account = Account(client);
-final user = await account.create(userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien");
-
-final session = await account.createEmailPasswordSession(email: 'me@appwrite.io', password: 'password');
-
+models.User user = await account.create(
+ userId: ID.unique(),
+ email: 'email@example.com',
+ password: 'password',
+ name: "Walter O'Brien",
+);
+
+models.Session session = await account.createEmailPasswordSession(
+ email: 'email@example.com',
+ password: 'password',
+);
```
-Fetch user profile:
+Fetch the signed-in user:
```dart
-Account account = Account(client);
+models.User profile = await account.get();
+```
-final profile = await account.get();
+List rows:
+
+```dart
+TablesDB tablesDB = TablesDB(client);
+
+models.RowList rows = await tablesDB.listRows(
+ databaseId: '',
+ tableId: '',
+ queries: [
+ Query.equal('published', true),
+ Query.limit(10),
+ ],
+);
```
-Upload File:
+Upload a file. On the web there is no file system path, so upload the picked file's bytes instead:
```dart
+import 'package:flutter/foundation.dart';
+
Storage storage = Storage(client);
-late InputFile file;
+InputFile input = kIsWeb
+ ? InputFile.fromBytes(bytes: pickedFile.bytes!, filename: 'image.jpg')
+ : InputFile.fromPath(path: './path-to-file/image.jpg');
+
+models.File file = await storage.createFile(
+ bucketId: '',
+ fileId: ID.unique(),
+ file: input,
+ permissions: [
+ Permission.read(Role.any()),
+ ],
+);
+```
-if(kIsWeb) {
- file = InputFile.fromBytes(bytes: pickedFile.bytes!, filename: 'image.jpg');
-} else {
- file = InputFile.fromPath(path: './path-to-file/image.jpg', filename: 'image.jpg');
-}
+Subscribe to realtime events:
-storage.createFile(
- bucketId: '[BUCKET_ID]',
- fileId: ID.unique(),
- file: file,
- permissions: [
- Permission.read(Role.any()),
- ],
-)
-.then((response) {
- print(response); // File uploaded!
-})
-.catchError((error) {
- print(error.response);
+```dart
+Realtime realtime = Realtime(client);
+
+RealtimeSubscription subscription = realtime.subscribe(['account']);
+
+subscription.stream.listen((event) {
+ print(event.payload);
});
```
-All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs)
+Keep the subscription while you need events, and call `subscription.close()` when you stop listening, for example in your widget's `dispose()`.
+
+Handle errors:
+
+```dart
+try {
+ models.User user = await account.get();
+} on AppwriteException catch (e) {
+ print('${e.code} ${e.type}: ${e.message}');
+}
+```
+
+All examples and API features are available in the [official Appwrite docs](https://appwrite.io/docs).