couchbase_lite 2.7.0+1 couchbase_lite: ^2.7.0+1 copied to clipboard
Flutter plugin for Couchbase Lite Community Edition, an embedded lightweight, noSQL database with live synchronization and offline support on Android and iOS.
couchbase_lite plugin #
A Flutter plugin for Couchbase Lite Community Edition. An embedded lightweight, noSQL database with live synchronization and offline support on Android and iOS.
The goal of this project is to align this library with the Swift SDK API for Couchbase Lite.
Note: This plugin is still under development, and some APIs might not be available yet. Feedback and Pull Requests are most welcome!
This project forked from Fluttercouch
Getting Started #
In your flutter project add the dependency:
dependencies:
couchbase_lite: ^2.7.0
flutter:
sdk: flutter
For help getting started with Flutter, view the online documentation
Supported Versions #
iOS #
Platform | Minimum OS version |
---|---|
iOS | 9.0 |
Android #
Platform | Runtime architectures | Minimum API Level |
---|---|---|
Android | armeabi-v7a | 19 |
Android | arm64-v8a | 21 |
Android | x86 | 19 |
API References #
Note: Syntax follows the Swift SDK but these are the SDKs used for the platform code.
Usage example #
Below is an example for the database using the BLoC pattern ( View <-> BLoC <-> Repository <-> Database )
The files can also be found in the plugin example but are not used in the main.dart. The example will be revised in the near future to use the BLoC pattern.
class AppDatabase {
AppDatabase._internal();
static final AppDatabase instance = AppDatabase._internal();
String dbName = "myDatabase";
List<Future> pendingListeners = List();
ListenerToken _replicatorListenerToken;
Database database;
Replicator replicator;
Future<bool> login(String username, String password) async {
try {
database = await Database.initWithName(dbName);
// Note wss://10.0.2.2:4984/my-database is for the android simulator on your local machine's couchbase database
ReplicatorConfiguration config =
ReplicatorConfiguration(database, "wss://10.0.2.2:4984/my-database");
config.replicatorType = ReplicatorType.pushAndPull;
config.continuous = true;
// Using self signed certificate
config.pinnedServerCertificate = "assets/cert-android.cer";
config.authenticator = BasicAuthenticator(username, password);
replicator = Replicator(config);
replicator.addChangeListener((ReplicatorChange event) {
if (event.status.error != null) {
print("Error: " + event.status.error);
}
print(event.status.activity.toString());
});
await replicator.start();
return true;
} on PlatformException {
return false;
}
}
Future<void> logout() async {
await Future.wait(pendingListeners);
await replicator.removeChangeListener(_replicatorListenerToken);
_replicatorListenerToken =
replicator.addChangeListener((ReplicatorChange event) async {
if (event.status.activity == ReplicatorActivityLevel.stopped) {
await database.close();
// Change listeners will be
//replicator.removeChangeListener(_replicatorListenerToken);
await replicator.dispose();
_replicatorListenerToken = null;
}
});
await replicator.stop();
}
Future<Document> createDocument(Map<String, dynamic> map) async {
var id = "mydocument::${Uuid().v1()}";
try {
var doc = MutableDocument(id: id, data: map);
if (await database.saveDocument(doc)) {
return doc;
} else {
return null;
}
} on PlatformException {
return null;
}
}
ObservableResponse<ResultSet> getMyDocument(String documentId) {
final stream = BehaviorSubject<ResultSet>();
// Execute a query and then post results and all changes to the stream
final Query query = QueryBuilder.select([
SelectResult.expression(Meta.id.from("mydocs")).as("id"),
SelectResult.expression(Expression.property("foo").from("mydocs")),
SelectResult.expression(Expression.property("bar").from("mydocs")),
])
.from(dbName, as: "mydocs")
.where(Meta.id.from("mydocs").equalTo(Expression.string(documentId)));
final processResults = (ResultSet results) {
if (!stream.isClosed) {
stream.add(results);
}
};
return _buildObservableQueryResponse(stream, query, processResults);
}
ObservableResponse<T> _buildObservableQueryResponse<T>(
BehaviorSubject<T> subject,
Query query,
ResultSetCallback resultsCallback) {
final futureToken = query.addChangeListener((change) {
if (change.results != null) {
resultsCallback(change.results);
}
});
final removeListener = () {
final newFuture = futureToken.then((token) async {
if (token != null) {
await query.removeChangeListener(token);
}
});
pendingListeners.add(newFuture);
newFuture.whenComplete(() {
pendingListeners.remove(newFuture);
});
};
try {
query.execute().then(resultsCallback);
} on PlatformException {
removeListener();
rethrow;
}
return ObservableResponse<T>(subject.debounce(Duration(seconds: 1)), () {
removeListener();
subject.close();
});
}
}
class ObservableResponse<T> implements StreamController<T> {
ObservableResponse(this._result, [this._onDispose]);
final Subject<T> _result;
final VoidCallback _onDispose;
@override
void add(data) => _result?.add(data);
@override
ControllerCallback get onCancel => throw UnsupportedError('ObservableResponses do not support cancel callbacks');
@override
ControllerCallback get onListen => throw UnsupportedError('ObservableResponses do not support listen callbacks');
@override
ControllerCallback get onPause => throw UnsupportedError('ObservableResponses do not support pause callbacks');
@override
ControllerCallback get onResume => throw UnsupportedError('ObservableResponses do not support resume callbacks');
@override
void addError(Object error, [StackTrace stackTrace]) => throw UnsupportedError('ObservableResponses do not support adding errors');
@override
Future addStream(Stream<T> source, {bool cancelOnError}) => throw UnsupportedError('ObservableResponses do not support adding streams');
@override
Future get done => _result?.done ?? true;
@override
bool get hasListener => _result?.hasListener ?? false;
@override
bool get isClosed => _result?.isClosed ?? true;
@override
bool get isPaused => _result?.isPaused ?? false;
@override
StreamSink<T> get sink => _result?.sink;
@override
Stream<T> get stream => _result?.stream;
@override
Future<dynamic> close() {
if (_onDispose != null) {
// Do operations here like closing streams and removing listeners
_onDispose();
}
return _result?.close();
}
@override
set onCancel(Function() onCancelHandler) => throw UnsupportedError('ObservableResponses do not support cancel callbacks');
@override
set onListen(void Function() onListenHandler) => throw UnsupportedError('ObservableResponses do not support listen callbacks');
@override
set onPause(void Function() onPauseHandler) => throw UnsupportedError('ObservableResponses do not support pause callbacks');
@override
set onResume(void Function() onResumeHandler) => throw UnsupportedError('ObservableResponses do not support resume callbacks');
}