> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter Install Guide

> Welcome to the Flutter SDK Installation Guide. This document provides step-by-step instructions for installing and configuring the Ditto SDK, enabling you to sync, query, and insert data seamlessly within your existing Flutter application.

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](/sdk/latest/quickstarts/flutter).

## Prerequisites

Before you begin, ensure you have the following installed:

* Code editor (preferably Visual Studio Code or Android Studio)
* Flutter SDK (>3.24.5)
* An existing Flutter application to integrate Ditto into
* Ditto Portal account with a Ditto App (see [Getting SDK Connection Details](/cloud/portal/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
  1. Navigate to the root folder
  2. Open `./android/settings.gradle`
  3. Update the `org.jetbrains.kotlin.android` plugin to `1.9.20`
     ```gradle {3} theme={null}
     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

### macOS

* macOS with the latest version of Xcode installed
* Requires additional network entitlements (see [Step 1](#step-1-add-the-ditto-dependency))

### Web

* Web support requires at least Flutter 3.24.5
* Follow the guide for [Web Browser Support](#step-6-web-browser-support)

## Step 1: Add the Ditto Dependency

<Steps>
  <Step>
    Run the following command in your terminal from the root of your application:

    ```shell theme={null}
    flutter pub add ditto_live
    flutter pub add permission_handler
    ```
  </Step>

  <Step>
    For iOS development, add the following permissions to `ios/Runner/Info.plist`:

    <CodeGroup>
      ```xml Info.plist theme={null}
      <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>
      ```
    </CodeGroup>
  </Step>

  <Step>
    For macOS development, add the following to `macos/Runner/Info.plist`:

    ```xml Info.plist theme={null}
    <key>NSBluetoothAlwaysUsageDescription</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>
    ```

    Also add the following entitlements to both `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`:

    <CodeGroup>
      ```xml DebugProfile.entitlements theme={null}
      <key>com.apple.security.network.client</key>
      <true/>
      <key>com.apple.security.network.server</key>
      <true/>
      ```

      ```xml Release.entitlements theme={null}
      <key>com.apple.security.network.client</key>
      <true/>
      <key>com.apple.security.network.server</key>
      <true/>
      ```
    </CodeGroup>

    <Note>
      These permissions are required for Ditto to work on macOS:

      * `NSLocalNetworkUsageDescription` and `NSBonjourServices` enable local network discovery for P2P sync
      * `com.apple.security.network.client` enables connecting to Ditto Cloud
      * `com.apple.security.network.server` enables peer-to-peer synchronization

      Without these, you may see errors like `Operation not permitted (os error 1)` when Ditto attempts to connect.
    </Note>
  </Step>

  <Step>
    For Android development, add the following permissions to `android/app/src/main/AndroidManifest.xml`.

    ```xml theme={null}
    <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.

    ```xml theme={null}
    <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>
</Steps>

## Step 2: Obtain Ditto Credentials

<Steps>
  <Step>
    Log in to your <a href="https://portal.ditto.live" target="_blank">Ditto Portal account</a>.
  </Step>

  <Step>
    Navigate to your database and obtain the Database ID and Playground Token. (See [Getting SDK Connection Details](/cloud/portal/getting-sdk-connection-details) for details)
  </Step>

  <Step>
    Locate the `databaseID` and `playground token` for your application. These are required to initialize the Ditto SDK.
  </Step>
</Steps>

## Step 3: Import & Initialize Ditto

<Steps>
  <Step>
    Import the SDK in your Dart file where you plan to use it.

    ```dart Dart theme={null}
    import 'package:ditto_live/ditto_live.dart';
    ```
  </Step>

  <Step>
    Request device permissions.

    <Warning>
      If you are writing code that targets both web and native platforms, you must
      not import platform-specific libraries, such as `dart:io`. However, this
      prevents using `Platform.is...` to detect the current platform. Note that this
      is a **compile-time** error, so it is **not** possible to work around this with
      `kIsWeb`.

      To streamline this, the Ditto Flutter SDK provides `Ditto.currentPlatform`,
      which is usable on all platforms.
    </Warning>

    ```dart theme={null}
    import "package:ditto_live/ditto_live.dart";
    import "package:permission_handler/permission_handler.dart";

    // ...

    final platform = Ditto.currentPlatform;

    if (platform case SupportedPlatform.android || SupportedPlatform.iOS) {
      await [
        Permission.bluetoothConnect,
        Permission.bluetoothAdvertise,
        Permission.nearbyWifiDevices,
        Permission.bluetoothScan
      ].request();
    }
    ```
  </Step>

  <Step>
    Initialize the SDK to obtain an instance of `Ditto`. This is the main entry point into all Ditto-related functionality.

    ```dart Dart theme={null}
    import 'package:ditto_live/ditto_live.dart';

    await Ditto.init();

    final databaseId = 'REPLACE_ME_WITH_YOUR_DATABASE_ID';
    final config = DittoConfig(
      databaseId: databaseId,
      connect: DittoConnect.server(
        url: 'https://$databaseId.cloud.ditto.live'
      ),
    );

    final ditto = await Ditto.open(config: config);

    ditto.auth?.expirationHandler = (ditto, secondsRemaining) async {
      final result = await ditto.auth?.login(
        'REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN',
        Authenticator.developmentProvider,
      );
      if (result?.error != null) {
        print('Auth error: ${result?.error}');
      }
    };

    // 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");

    await ditto.sync.start();
    ```
  </Step>
</Steps>

## 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`:

```dart Dart theme={null}
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.

```dart Dart theme={null}
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:

```dart Dart theme={null}
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.

```dart Dart theme={null}
@override
void initState() {
  super.initState();
  observeData();
}
```

Remember to cancel your observer in your widget’s `dispose()` method to prevent resource leaks

```dart Dart theme={null}
@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 `start` on the sync object. This enables this device to sync data with other peers including the Ditto Cloud.

```dart Dart theme={null}
await ditto.sync.start();
```

### 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:

```dart Dart theme={null}
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:

```dart theme={null}
final ditto = 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 supports 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.

```dart theme={null}

// Only enable P2P transports on mobile or desktop
if (!kIsWeb) {
  ditto.updateTransportConfig((builder) {
    builder.peerToPeer = PeerToPeer.all();
  });
}

await ditto.sync.start();

```

### 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](https://docs.flutter.dev/platform-integration/web/renderers#build-modes)
  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: Devtools Extension

The Ditto Flutter SDK has a [Devtools
extension](https://docs.flutter.dev/tools/devtools/extensions). This extension
provides a DQL editor, which allows you to execute DQL queries against the
Ditto instance running in your app.

To access it, open Flutter Devtools by pressing `v` in your terminal after
`flutter run`-ing your app. Then, in the top bar of the devtools window, click
on `ditto_live` (depending on the size of your browser window, it may be in a
drop-down menu). The first time you open the Ditto devtools extension, you will
need to grant it permission.

Once it's running, it will connect to your app and communicate with the first
`Ditto` instance that your app created. If no `Ditto` instances have been
created, it will not load.

The extension has two text fields - one for the query, and one for the
arguments. Arguments are parsed as JSON5, which is compatible with JSON, but
has some ergonomic improvements, such as:

* C-style comments with '//'
* allowed trailing commas in arrays and objects
* optional quotes in object keys

## Step 8: Working with Hot Restart

Flutter's **hot reload** is fully compatible with Ditto and works as expected for UI changes. However, **hot restart** can cause issues because Ditto maintains persistent state that conflicts with Flutter's restart mechanism.

### Understanding the Issue

When you perform a hot restart in Flutter:

* The Dart VM resets and reinitializes your app
* Ditto's native resources and lockfiles remain in memory/disk
* Attempting to reinitialize Ditto with the same persistence directory causes conflicts

### Recommended Workarounds

#### Option 1: Use Randomized Directory in Debug Mode

The simplest solution is to use a different persistence directory for each debug session. This prevents conflicts by ensuring each restart uses a fresh directory:

```dart theme={null}
import 'package:ditto_live/ditto_live.dart';
import 'dart:math';

Future<Ditto> initializeDitto() async {
  // Provide your own logic to decide what sort of build this is. See below for
  // common ways to set this value.
  final isProduction = checkIsProductionBuild();

  final persistenceDirectory = switch (isProduction) {
    true => 'ditto',
    false => 'ditto_debug_${Random().nextInt(1 << 32)}',
  };

  final ditto = await Ditto.init(
    identity: yourIdentity,
    persistenceDirectory: persistenceDirectory,
  );

  return ditto;
}
```

You should consider how you want to decide whether to initialize Ditto with a
randomized directory. Common ways to set this value are:

* `--dart-define` and `const String.fromEnvironment`. See [this
  page](https://dart.dev/libraries/core/environment-declarations#flutter) for
  more details.
* Flutter "flavors" - Once a flavor is configured, you can check it in Dart by
  reading the `appFlavor` global. See [this
  page](https://docs.flutter.dev/deployment/flavors) for more details.
* By checking the current build mode - i.e. `kDebugMode`, `kReleaseMode`, etc.

<Note>
  This approach means data won't persist between hot restarts during development.
  This is typically acceptable for development workflows since you're focusing on
  UI/logic changes rather than data persistence. If you need data persistence,
  you **must** do a full restart of the app.
</Note>

### Best Practices

* **Use hot reload** for UI changes - it's fully compatible with Ditto
* **Use randomized directories** in debug mode for the smoothest development experience
* **Implement error handling** to gracefully recover from initialization failures
* **Document your approach** in your project README so team members understand the development workflow

<Note>
  **Note on Flutter Web Hot Reload:** Flutter's built-in hot reload for web applications requires at least Flutter 3.32 (experimental with a flag) or 3.35+ (enabled by default). This is a Flutter framework capability, not a Ditto compatibility requirement. Ditto supports Flutter 3.24.5-3.32+ as documented in the [compatibility matrix](/sdk/latest/compatibility/flutter).
</Note>

## Step 9: Troubleshooting

* Ensure that all dependencies are up-to-date by updating to the latest version.
* Contact the Ditto team through [Contact Us](https://support.ditto.com)
