Merge pull request #303 from appwrite/dev

feat: Flutter SDK update for version 22.0.0
This commit is contained in:
Chirag Aggarwal
2026-03-03 16:46:31 +05:30
committed by GitHub
13 changed files with 55 additions and 53 deletions
+3 -7
View File
@@ -1,13 +1,9 @@
# Change Log
## 21.4.0
## 22.0.0
* Added upsert() to DocumentChannel and RowChannel to support upsert operations on documents and rows.
* Added Query.contains, Query.containsAny, and Query.containsAll for enhanced filtering capabilities.
## 21.3.0
* Added memberships realtime channel helper
* Breaking: Channel API no longer defaults to '*'; explicit IDs required.
* Updated: Docs and README reflect 21.4.1; TTL examples added.
## 21.1.0
+2 -2
View File
@@ -7,7 +7,7 @@
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)
**This SDK is compatible with Appwrite server version 1.8.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-flutter/releases).**
**This SDK is compatible with Appwrite server version latest. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-flutter/releases).**
Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Flutter SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
@@ -19,7 +19,7 @@ Add this to your package's `pubspec.yaml` file:
```yml
dependencies:
appwrite: ^21.4.0
appwrite: ^22.0.0
```
You can install packages from the command line:
@@ -13,5 +13,6 @@ DocumentList result = await databases.listDocuments(
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
ttl: 0, // optional
);
```
+1
View File
@@ -13,5 +13,6 @@ RowList result = await tablesDB.listRows(
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
ttl: 0, // optional
);
```
+17 -11
View File
@@ -30,7 +30,13 @@ class _Membership {}
class _Resolved {}
// Helper function for normalizing ID
String _normalize(String id) => id.trim().isEmpty ? '*' : id.trim();
String _normalize(String id) {
final trimmed = id.trim();
if (trimmed.isEmpty) {
throw ArgumentError('Channel ID is required');
}
return trimmed;
}
/// Channel class with generic type parameter for type-safe method chaining
class Channel<T> {
@@ -58,25 +64,25 @@ class Channel<T> {
String toString() => _segments.join('.');
// --- ROOT FACTORIES ---
static Channel<_Database> database([String id = '*']) =>
static Channel<_Database> database(String id) =>
Channel<_Database>._(['databases', _normalize(id)]);
static Channel<_TablesDB> tablesdb([String id = '*']) =>
static Channel<_TablesDB> tablesdb(String id) =>
Channel<_TablesDB>._(['tablesdb', _normalize(id)]);
static Channel<_Bucket> bucket([String id = '*']) =>
static Channel<_Bucket> bucket(String id) =>
Channel<_Bucket>._(['buckets', _normalize(id)]);
static Channel<_Execution> execution([String id = '*']) =>
static Channel<_Execution> execution(String id) =>
Channel<_Execution>._(['executions', _normalize(id)]);
static Channel<_Func> function([String id = '*']) =>
static Channel<_Func> function(String id) =>
Channel<_Func>._(['functions', _normalize(id)]);
static Channel<_Team> team([String id = '*']) =>
static Channel<_Team> team(String id) =>
Channel<_Team>._(['teams', _normalize(id)]);
static Channel<_Membership> membership([String id = '*']) =>
static Channel<_Membership> membership(String id) =>
Channel<_Membership>._(['memberships', _normalize(id)]);
static String account() => 'account';
@@ -95,8 +101,8 @@ class Channel<T> {
/// Only available on Channel<_Database>
extension DatabaseChannel on Channel<_Database> {
Channel<_Collection> collection([String? id]) =>
_next<_Collection>('collections', id ?? '*');
Channel<_Collection> collection(String id) =>
_next<_Collection>('collections', id);
}
/// Only available on Channel<_Collection>
@@ -109,7 +115,7 @@ extension CollectionChannel on Channel<_Collection> {
/// Only available on Channel<_TablesDB>
extension TablesDBChannel on Channel<_TablesDB> {
Channel<_Table> table([String? id]) => _next<_Table>('tables', id ?? '*');
Channel<_Table> table(String id) => _next<_Table>('tables', id);
}
/// Only available on Channel<_Table>
+3 -1
View File
@@ -124,7 +124,8 @@ class Databases extends Service {
required String collectionId,
List<String>? queries,
String? transactionId,
bool? total}) async {
bool? total,
int? ttl}) async {
final String apiPath =
'/databases/{databaseId}/collections/{collectionId}/documents'
.replaceAll('{databaseId}', databaseId)
@@ -134,6 +135,7 @@ class Databases extends Service {
if (queries != null) 'queries': queries,
if (transactionId != null) 'transactionId': transactionId,
if (total != null) 'total': total,
if (ttl != null) 'ttl': ttl,
};
final Map<String, String> apiHeaders = {};
+3 -1
View File
@@ -120,7 +120,8 @@ class TablesDB extends Service {
required String tableId,
List<String>? queries,
String? transactionId,
bool? total}) async {
bool? total,
int? ttl}) async {
final String apiPath = '/tablesdb/{databaseId}/tables/{tableId}/rows'
.replaceAll('{databaseId}', databaseId)
.replaceAll('{tableId}', tableId);
@@ -129,6 +130,7 @@ class TablesDB extends Service {
if (queries != null) 'queries': queries,
if (transactionId != null) 'transactionId': transactionId,
if (total != null) 'total': total,
if (ttl != null) 'ttl': ttl,
};
final Map<String, String> apiHeaders = {};
+1 -1
View File
@@ -40,7 +40,7 @@ class ClientBrowser extends ClientBase with ClientMixin {
'x-sdk-name': 'Flutter',
'x-sdk-platform': 'client',
'x-sdk-language': 'flutter',
'x-sdk-version': '21.4.0',
'x-sdk-version': '22.0.0',
'X-Appwrite-Response-Format': '1.8.0',
};
+1 -1
View File
@@ -58,7 +58,7 @@ class ClientIO extends ClientBase with ClientMixin {
'x-sdk-name': 'Flutter',
'x-sdk-platform': 'client',
'x-sdk-language': 'flutter',
'x-sdk-version': '21.4.0',
'x-sdk-version': '22.0.0',
'X-Appwrite-Response-Format': '1.8.0',
};
+1 -1
View File
@@ -5,7 +5,7 @@ class Document implements Model {
/// Document ID.
final String $id;
/// Document automatically incrementing ID.
/// Document sequence ID.
final int $sequence;
/// Collection ID.
+1 -1
View File
@@ -5,7 +5,7 @@ class Row implements Model {
/// Row ID.
final String $id;
/// Row automatically incrementing ID.
/// Row sequence ID.
final int $sequence;
/// Table ID.
+1 -1
View File
@@ -1,5 +1,5 @@
name: appwrite
version: 21.4.0
version: 22.0.0
description: Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API
homepage: https://appwrite.io
repository: https://github.com/appwrite/sdk-for-flutter
+20 -26
View File
@@ -3,18 +3,13 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
group('database()', () {
test('returns database channel with defaults', () {
expect(Channel.database().collection().document().toString(),
'databases.*.collections.*.documents');
test('throws when database id is missing', () {
expect(() => Channel.database(''), throwsArgumentError);
});
test('returns database channel with specific IDs', () {
expect(
Channel.database('db1')
.collection('col1')
.document('doc1')
.toString(),
'databases.db1.collections.col1.documents.doc1');
expect(Channel.database('db1').collection('col1').document().toString(),
'databases.db1.collections.col1.documents');
});
test('returns database channel with action', () {
@@ -39,14 +34,13 @@ void main() {
});
group('tablesdb()', () {
test('returns tablesdb channel with defaults', () {
expect(Channel.tablesdb().table().row().toString(),
'tablesdb.*.tables.*.rows');
test('throws when tablesdb id is missing', () {
expect(() => Channel.tablesdb(''), throwsArgumentError);
});
test('returns tablesdb channel with specific IDs', () {
expect(Channel.tablesdb('db1').table('table1').row('row1').toString(),
'tablesdb.db1.tables.table1.rows.row1');
expect(Channel.tablesdb('db1').table('table1').row().toString(),
'tablesdb.db1.tables.table1.rows');
});
test('returns tablesdb channel with action', () {
@@ -67,13 +61,13 @@ void main() {
});
group('bucket()', () {
test('returns buckets channel with defaults', () {
expect(Channel.bucket().file().toString(), 'buckets.*.files');
test('throws when bucket id is missing', () {
expect(() => Channel.bucket(''), throwsArgumentError);
});
test('returns buckets channel with specific IDs', () {
expect(Channel.bucket('bucket1').file('file1').toString(),
'buckets.bucket1.files.file1');
expect(
Channel.bucket('bucket1').file().toString(), 'buckets.bucket1.files');
});
test('returns buckets channel with action', () {
@@ -83,8 +77,8 @@ void main() {
});
group('functions()', () {
test('returns functions channel with defaults', () {
expect(Channel.function().toString(), 'functions.*');
test('throws when function id is missing', () {
expect(() => Channel.function(''), throwsArgumentError);
});
test('returns functions channel with specific IDs', () {
@@ -93,8 +87,8 @@ void main() {
});
group('executions()', () {
test('returns executions channel with defaults', () {
expect(Channel.execution().toString(), 'executions.*');
test('throws when execution id is missing', () {
expect(() => Channel.execution(''), throwsArgumentError);
});
test('returns executions channel with specific IDs', () {
@@ -103,8 +97,8 @@ void main() {
});
group('teams()', () {
test('returns teams channel with default', () {
expect(Channel.team().toString(), 'teams.*');
test('throws when team id is missing', () {
expect(() => Channel.team(''), throwsArgumentError);
});
test('returns teams channel with specific team ID', () {
@@ -117,8 +111,8 @@ void main() {
});
group('memberships()', () {
test('returns memberships channel with default', () {
expect(Channel.membership().toString(), 'memberships.*');
test('throws when membership id is missing', () {
expect(() => Channel.membership(''), throwsArgumentError);
});
test('returns memberships channel with specific membership ID', () {