This guide is for integrating Ditto into an existing Flutter application. If you’re looking to get started quickly with a brand new Flutter app, follow the Flutter QuickStart.
Prerequisites
Before you begin, ensure you have the following installed:
- Code editor (preferably Visual Studio Code or Android Studio)
- Flutter SDK (version 3.19.0 - 3.24.5)
- An existing Flutter application to integrate Ditto into
- Ditto Portal account with a Ditto App (see Getting SDK Connection Details)
Android
- Android Studio installed on your machine
- A physical Android device or Android emulator
- Upgrade your Kotlin Gradle version to
1.9.20
or later
- Navigate to the root folder
- Open
./android/settings.gradle
- Update the
org.jetbrains.kotlin.android
plugin to 1.9.20
plugins {
// ...
id "org.jetbrains.kotlin.android" version "1.9.20" apply false
}
iOS
- macOS with the latest version of Xcode installed
- A physical iOS device or an iOS simulator
Web
- Ensure you have Flutter SDK version 3.22.0 - 3.32.6 installed
- Follow the guide for Web Browser Support
Linux
- Make sure you have followed the official guide
for developing Flutter Linux apps.
Handling Linux Binaries
On Linux, ditto_live
will load a dynamic library called libdittoffi.so
. By
default, it will be included in builds produced by the flutter
CLI.
However, if you want to configure this path, you can set $LIBDITTOFFI_PATH
.
This may be required by some package managers, for example.
When running unit/widget tests with flutter test
, binaries will not be
provided, and you will get an error. In order to make flutter test
work, you
must set both $LIBDITTOFFI_PATH
and $XDG_DOCUMENTS_DIR
.
Your desktop environment may already set $XDG_DOCUMENTS_DIR
, but if it does
not, $HOME/Documents
is a sensible default. For more information, see the
this wiki page on XDG directories.
Here is an example of how you might run tests that depend on ditto_live
version 4.12.0
on Linux:
export XDG_DOCUMENTS_DIR="$HOME/Documents"
export LIBDITTOFFI_PATH="$HOME/libdittoffi.so"
VERSION="4.12.0"
ARCH="x86_64" # replace with "aarch64" on ARM
DOWNLOAD_URL="https://software.ditto.live/flutter/ditto/$VERSION/$ARCH/libdittoffi.so"
curl -0 "$DOWNLOAD_URL" -o "$LIBDITTOFFI_PATH"
flutter test
Step 1: Add the Ditto Dependency
Run the following command in your terminal from the root of your application:
flutter pub add ditto_live
flutter pub add permission_handler
# If you need a specific version of `ditto_live` add it with the following line
# Note: do NOT use '^a.b.c', since that will request any semver-compatible
version with a.b.c, and will not pin your project to exactly a.b.c
flutter pub add ditto_live:'a.b.c'
For iOS development, add the following permissions to ios/Runner/Info.plist
:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Uses Bluetooth to connect and sync with nearby devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Uses Bluetooth to connect and sync with nearby devices</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Uses WiFi to connect and sync with nearby devices</string>
<key>NSBonjourServices</key>
<array>
<string>_http-alt._tcp.</string>
</array>
For Android development, add the following permissions to android/app/src/main/AndroidManifest.xml
.
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="tiramisu" />
Additionally, if you would like Ditto to continue syncing data while the Android app is running in the background add the following permissions and service declaration to android/app/src/main/AndroidManifest.xml
. Then edit the service metadata appropriately.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" tools:targetApi="upside_down_cake" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" tools:targetApi="tiramisu" />
<application ...>
<service
android:name="live.ditto.transports.foregroundservice.DefaultDittoForegroundService"
android:exported="false"
android:foregroundServiceType="connectedDevice"
android:stopWithTask="true">
<meta-data android:name="notificationChannelName" android:value="Background Data Sync" />
<meta-data android:name="notificationChannelId" android:value="BackgroundDataSyncId" />
<meta-data android:name="smallIcon" android:resource="@drawable/small_app_icon" />
<meta-data android:name="notificationTitle" android:value="Syncing Data" />
<meta-data android:name="notificationText" android:value="Syncing data across connected devices" />
<meta-data android:name="notificationId" android:value="1001" />
</service>
</application>
Make sure you customize the service metadata—it will be displayed to the user in the foreground service notification. Note that your small_app_icon
drawable must be monochrome white.
The foreground service will be automatically started and stopped on startSync()
and stopSync()
, respectively.
Step 2: Obtain Ditto Credentials
Locate the appID
, playground token
, authURL
, and websocketURL
for your application. These are required to initialize the Ditto SDK.
Step 3: Import & Initialize Ditto
Import the SDK in your Dart file where you plan to use it.
import 'package:ditto_live/ditto_live.dart';
Import these libraries to request device permissions.
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
Initialize the SDK to obtain an instance of Ditto
. This is the main entry point into all Ditto-related functionality.
await Ditto.init();
final identity = OnlinePlaygroundIdentity(
appId: "REPLACE_ME_WITH_YOUR_APP_ID",
token: "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN",
customAuthUrl: "REPLACE_ME_WITH_YOUR_AUTH_URL",
enableDittoCloudSync: false // This is required to be set to false to use the correct URLs
);
final ditto = await Ditto.open(identity);
ditto.updateTransportConfig((config) {
// Set the Ditto Websocket URL
config.connect.webSocketUrls.add("wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL");
});
// Disable DQL strict mode so that collection definitions are not required in DQL queries
await ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");
ditto.startSync();
Step 4: Creating and Reading Data
Insert Data
To insert data into the Ditto store, use the following example. This demonstrates inserting a document into a collection named items
:
Future<void> insertData() async {
final item = {'name': 'Sample Item', 'value': 100};
await ditto.store.execute(
"INSERT INTO items VALUES (:item)",
arguments: {"item": item},
);
}
Call the insertData
function wherever appropriate in your application, such as in a button press handler.
FloatingActionButton(
onPressed: insertData,
child: Icon(Icons.add),
)
Obseve Data Store Changes
To observe changes in the data store, register an observer. This example sets up an observer to listen for changes in the items
collection:
void observeData() {
final observer = ditto.store.registerObserver(
"SELECT * FROM items",
onChange: (event) {
// Handle data changed event
print("data changed");
},
);
}
Call the observeData
function, typically in the initState
method of a stateful widget to start observing when the widget is initialized.
@override
void initState() {
super.initState();
observeData();
}
Remember to cancel your observer in your widget’s dispose()
method to prevent resource leaks
@override
void dispose() {
_observer.cancel();
super.dispose();
}
Step 5: Syncing Data
To keep your data in sync across devices and with Ditto Server, you need to register sync subscriptions. Sync subscriptions define the specific data that should be automatically synchronized to the device.
Starting Sync
To start sync on the device you need to call startSync
. This enables this device to sync data with other peers including the Ditto Cloud.
Registering a Subscription
Register a subscription to a collection to keep the data in sync. Below is an example of how to register a subscription to the items
collection:
void syncData() {
ditto.sync.registerSubscription("SELECT * FROM items");
}
Call the syncData
function, typically when initializing your Ditto instance.
Syncing Data Offline
Once your application is syncing data using Ditto, you can deploy it to multiple
local devices, Android emulators, or iOS simulators. You can then disable the
internet to observe the devices syncing offline.
Step 6: Web Browser Support
Web Assets
When Ditto is used on the web, it needs to download and initialize assets for
its WebAssembly binary. This happens automatically when calling await Ditto.init()
. By default, assets are bundled with the application and loaded
from the same web server that serves the application. After running flutter build web
, you can find those in the
build/web/assets/packages/ditto_live/lib/assets
directory.
Alternatively, you can host the contents of that directory on a CDN or other web
server and configure Ditto to load assets from there. All contents of the
directory, excluding the ditto.wasm
file itself, must be reachable at the URL
given by the wasmShimUrl
parameter. The ditto.wasm
file can be served from a
separate URL given by the wasmUrl
parameter.
Configuration for loading all assets from a CDN:
await Ditto.init(
wasmUrl: 'https://your-cdn.com/ditto-assets/ditto.wasm',
wasmShimUrl: 'https://your-cdn.com/ditto-assets/',
);
Please consider your application’s loading UI while downloading the assets as it
may take a few seconds to download and initialize the WebAssembly binary.
It is strongly recommended to serve assets using a web server or CDN that
supports compression. This will significantly reduce the download size of the
assets and thereby the loading time of your application.
Disable Peer to Peer
On the web, Ditto only eupports syncing with the Ditto cloud and does not
directly connect to other devices. Make sure that your app only enables
peer-to-peer transports on mobile platforms.
final ditto = await Ditto.init(
wasmUrl: 'https://your-cdn.com/ditto-assets/ditto.wasm',
wasmShimUrl: 'https://your-cdn.com/ditto-assets/',
);
final connect = Connect(webSocketUrls: {"wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL"});
// Only enable P2P transports on mobile or desktop
if (!kIsWeb) {
final peerToPeer = PeerToPeer.all();
}
ditto.transportConfig = TransportConfig(peerToPeer: peerToPeer, connect: connect);
ditto.startSync();
Considerations for Web
- Flutter for Web is designed to work seamlessly with the Ditto cloud. Due to
browser restrictions, direct peer-to-peer synchronization with other devices
is not supported.
- The web platform utilizes an in-memory Ditto store, meaning data is not
retained across page reloads.
- Ditto’s Flutter SDK currently supports the default build
mode
for the web.
- When developing with Flutter’s development server, it is required to quit and
restart the server after making changes to the source code of your app
(support for hot restart is in development).
Step 7: Troubleshooting
- Ensure that all dependencies are up-to-date by updating to the latest version.
- Contact the Ditto team through Contact Us