4.11.0
Release Date: June 10, 2025

4.11 - DQL & Legacy Compatibility

With 4.11 we are excited to introduce legacy compatibility for DQL, which allows developers to use the new DQL APIs while maintaining compatibility with existing code and data. This capability, called STRICT MODE, is designed to ease the transition to the new APIs and ensure that existing applications continue to function as expected.

DQL Strict Mode

DQL Strict Mode provides control over how DQL statements are evaluated and how data types are inferred. When disabled, it offers more flexibility in working with documents without requiring explicit collection definitions.

For more information see DQL Strict Mode

With strict mode disabled (DQL_STRICT_MODE=false), Ditto is more flexible and will infer the CRDT type based on the document’s shape:

  • Objects are treated as CRDT maps
  • Scalars and arrays are treated as Registers
  • Counters and attachments are inferred from operations
  • Collection definitions are no longer required

Setting DQL Strict Mode

DQL
ALTER SYSTEM SET DQL_STRICT_MODE = false

DQL Differ

DQL 4.11 introduces a new Differ API for calculating diffs between query results. This API can be used with a store observer to understand how its results change over successive callbacks. The Differ API provides a way to track changes in the data and can be used to optimize data synchronization and conflict resolution.

Differ API documentation.

DQL Transactions

DQL 4.11 introduces support for transactions. Transactions can be used to group multiple DQL statements together, ensuring that either all of the statements are executed successfully or none of them are. This is particularly useful in scenarios where multiple updates or inserts need to be made to the database, and you want to ensure that either all of them are applied or none of them are.

Transactions API documentation.

Advanced DQL Features

DQL 4.11 introduces numerous advanced querying and manipulation features:

FeatureDescriptionExample
UNSET clauseRemove fields from documentsUPDATE orders UNSET items.abc WHERE _id = 'my-id'
CAST functionExplicitly convert between data typesSELECT * FROM products WHERE CAST(price AS INT) > 10
USE IDS clauseDirect document retrieval by IDSELECT * FROM products USE IDS '123', '456'
SIMILAR TOPattern matching expressionsSELECT * FROM products WHERE name SIMILAR TO '%phone%'
Runtime expressionsUse expressions in object/array constructionUPDATE products SET metadata = { 'updated_at': current_timestamp() }
CountersSupport for legacy compatible counter typeUPDATE products APPLY in_stock PN_INCREMENT BY 1.0

In 4.11, functional operations are denoted with an arrow (->) are deprecated. Read more.

Tombstone Reaper

The tombstone reaper is now enabled by default for all SDKs. This feature helps clean up deleted document markers (tombstones) to improve database performance and reduce storage requirements.

The reaper:

  • Automatically removes tombstones after a configurable time-to-live (TTL)
  • Uses read transactions for efficiency
  • Includes random scheduling variations to prevent performance problems
  • Tracks the number of tombstones evicted for monitoring
  • Remembers previous reap times across restarts

This improves overall system performance by reducing the long-term accumulation of tombstones when calling DELETE to remove documents.

4.11.0 Common Changelog

Added

  • Introduced DQL_STRICT_MODE as a system parameter to opt-out of requiring collection definitions (#16573)
  • Added transports_ble_adapter_mac system parameter to allow configuring bluetooth adapter MAC address (#16159)
  • Small peers now log the result of OnlinePlayground authentication, including if the supplied shared token was incorrect (#16301)
  • A cumulative running total of the number of tombstones ever evicted by the reaper to Small Peer Info (#16424)
  • Added system parameters to fine-tune Ditto behavior when it encounters a locked persistence directory (#CORE-479)
  • New DQL CAST function to explicitly cast between types (#QE-108)
  • Conditional functions for unknowns (#QE-111)
  • DQL DATE processing functions (#QE-112)
  • New DQL clause USE IDS for direct document retrieval by id (#QE-114)
  • Additional string functions (#QE-124)
  • SIMILAR TO filter expression (#QE-125)
  • PN_COUNTER support (APPLY clause in UPDATE statement) (#QE-127)
  • UNSET clause to the UPDATE statement (#QE-128)
  • MERGE as an alias for UPDATE in the ON ID CONFLICT clause of an INSERT statement (#QE-132)
  • The ability to use runtime-evaluated expressions in object constructs in DQL statements (#QE-133)
  • The ability to use runtime-evaluated expressions in array constructs in DQL statements (#QE-134)
  • Static expressions in mutators (#QE-139)
  • Additional object manipulation DQL scalar functions (#QE-159)
  • EXPLAIN for query plan (#QE-105)
  • More detailed messages about network problems are logged when authentication fails (#SEC-124)

Changed

  • Use the modern rustls-platform-verifier TLS crate on all platforms rather than rustls-native-certs (#15956)
  • The tombstone reaper scheduling to remember the previous reap times across shutdown and restart of the small peer (#16237)
  • The tombstone reaper scheduling code to add random variations, which helps prevent performance problems if/when all peers run the reaper simultaneously (#16286)
  • The tombstone reaper to be enabled by default for all SDKs (#16350)
  • INSERT to now accept either DOCUMENTS or VALUES as the keyword introducing the list of values to insert (#QE-140)
  • Date functions accept “local” as a timezone & use the executing peer’s local timezone (#QE-168)
  • Unified dedicated TCP servers across different transports (Multicast, mDNS, and WiFi Aware) by utilizing a single static TCP server implementation (#TRANS-131)

Fixed

  • Fixed issue where user_collection_sync_scopes could not be updated after initial set (#16299)
  • Fixed: A rare crash on Android devices when shutting down Bluetooth. (#17069)
  • Fixed: Bluetooth connectivity on Apple devices works when background Bluetooth permissions are missing. (#17065)

Removed

  • Old multihop networking stack and system parameter network_use_nextgen_multihop_stack (#16370)
  • TLS certificate compression, which led to incompatibilities (#SDKS-1046)

Performance

  • The tombstone reaper uses read transactions for some of its work instead of holding open a long-running write transaction, thus reducing contention on the small peer database (#16338)
  • Improved speed and efficiency of the tombstone reaping process (#16535)

4.11.0 Flutter Specific Changes

  • Added DittoDiff class to represent diffs produced by the DittoDiffer
  • Added DittoDiffer class for calculating diffs between query results
  • Added transaction() method on DittoStore for performing DQL transactions
  • Added DittoTransaction class to represent an active DQL transaction
  • Added DittoTransactionInfo class for transaction information
  • Added DittoTransactionCompletionAction for commit/rollback actions
4.10.4
Release Date: June 3, 2025

4.10.4 Security & Stability Improvements

We’ve enhanced our TLS certificate handling to provide more robust and reliable secure connections while maintaining our high security standards.

  • Improved reliability of secure connections by gracefully handling certificate validation challenges
  • Enhanced network stability with smarter handling of security certificates
  • Added flexibility in secure connection handling while maintaining robust security standards
4.10.3
Release Date: May 6, 2025

4.10.3 Stability Improvements, DQL Enhancements

We’ve enhanced our query capabilities with improved sorting and filtering for UPDATE operations, while also focusing on overall reliability improvements.

  • Added powerful sorting and filtering capabilities to UPDATE operations, giving you more control over how you modify data on Small Peer devices
  • Enhanced reliability and performance of data queries across your application
4.10.2
Release Date: May 1, 2025

4.10.2

  • No significant changes. Bumped version to 4.10.2 to align with other SDKs.
4.10.1
Release Date: March 26, 2025

4.10.1

  • Added support for the web platform
  • Attachment fetchers now successfully complete the attachment fetch operation
  • The isStopped property of AttachmentFetcher instances now becomes true after completing an attachment fetch
  • Resolved a memory leak that affected QueryResult and QueryResultItem
  • ditto.store.newAttachment now returns a Future<Attachment> rather than an Attachment
  • ditto.close() now returns a Future<void> rather than void
4.10.0
Release Date: March 17, 2025

4.10.0 New Capabilities

Now Supporting Flutter 3.19+

Ditto’s Flutter SDK now supports Flutter 3.19 and later, broadening compatibility and making it easier for more developers to integrate Ditto into their apps. This update ensures greater flexibility, allowing teams to adopt Ditto without needing to upgrade immediately to the latest Flutter version.

Experimental Support for Hot Restart in Flutter

The Ditto team is excited to preview support for Hot Restarts in Flutter.

To allow users experiment and provide feedback on hot restarts we’ve shipped a specific SDK version with hot restarts enabled by default.

To use hot restarts update your version to 4.10.0-experimental-hot-restart.0. This version is identical to 4.10.0 but has Hot Restart enabled by default. In a upcoming release this capability will be integrated into Ditto’s standard package as a developer mode config that can be set by the user on Ditto class initialization.

Flutter Dart has limited support for Hot Restarts with native binaries, such as Ditto. The Ditto team is working to mitigate these issues. If you run into any issues while experimenting with Flutter Hot Restarts reach out to Ditto Customer Support.

Known issues include:

  • Logging must be disabled. To do this set DittoLogger.isEnabled = false immediately after calling await Ditto.init().
  • Attempting to use attachments will cause a crash.
  • Hot restarting will leak some resources, notably memory. In our testing, approximately 20-30MB is leaked per hot-restart.
  • Data integrity cannot be guaranteed. You may experience data corruption when hot-restarting, which may be propagated to any peers that are being synced with.
  • Sporadic crashes can occur.
Hot Restarts is a developer mode config and can cause application instability, especially for long running sessions. Do not ship to any production environment using version 4.10.0-experimental-hot-restart.0.

4.10.0 Flutter Specific Changelog

  • Add support for Flutter 3.19 (Flutter - support 3.19 #16080)
  • Throw an error when trying to use Ditto APIs before awaiting Ditto.init() (#15880)
  • persistenceDirectory now supports relative paths and no longer needs to exist, and also works on web (#15890)
  • compiling with —wasm now works. Required for upcoming Flutter Web release. (#15991)
  • LAN transport shuts down when stopSync() is called (#SDKS-707)
4.9.2
Release Date: Feb 26, 2024

4.9.2

Fixed: Fixed a bug that caused observer events to not be triggered in profile and release mode (either via the onChange parameter or the StoreObserver.changes stream)

4.9.1
Release Date: Feb 24, 2024

4.9.1

Added: Support for Flutter 3.19 for iOS and Android

Changed: Failing to call await Ditto.init() will now throw. This is to prevent improper initialization.

Changed: ditto.transportsConfig = config; will now throw if config enables transports which don’t exist on the current platform (e.g. AWDL on Android or P2P Wifi on iOS).

TransportConfig.setAllPeerToPeerEnabled() will now take the current platform into account, and will only modify transports which are available. A newly created Ditto instance will not have transports enabled that are not available on the current platform

Fixed: Resolve an uncaught exception that could happen in ditto.presence.graph when there were P2PWifi connections.

Fixed: The persistence directory behaves differently if a relative path is provided.

If a relative path is provided, it will be treated as a subdirectory of getApplicationDocumentsDirectory(). If an absolute path is given, its value is not modified. The persistenceDirectory parameter in Ditto.open is now optional, and defaults to "ditto". If the directory does not exist, it will be created.

4.9.0 (GA Release)
Release Date: Jan 27, 2024

4.9.0 (GA Release)

We’re excited to announce the General Availability of the Ditto Flutter SDK! Designed to empower developers building cross-platform apps, the Flutter SDK delivers reliable real-time data synchronization and offline-first capabilities, now production-ready!

API Support Guarantees

General Availability (GA) means API stability! Starting with 4.9.0 Ditto will guarantee semver support of all APIs following industry standards. For more on semver see semver.org.

Changes from the Public Preview

To provide the best product with our GA release we’ve made some final modifications to the underlying architecture that bridges Dart and the Ditto Core for Android. The goal with this change is to provide a higher quality product with improved performance, reliability, and developer experience.

These changes provide the following benefits:

  • Enhanced developer ergonomics through synchronous APIs
  • Improved performance for all operations across all platforms
  • Reduced complexity improving reliability
  • Reduced background operations for better battery life
  • Lower memory consumption

Breaking Changes

This release contains a handful of minor syntax changes to provide an improved developer experience and better support the underlying framework. These syntax changes all stem from the ability to support non-async calls across the Dart-Ditto Core FFI.

In the initial Dart-Ditto implementation all Dart internal calls were required to make async calls into Ditto. This required us to make all Ditto API methods async. Due to the improvements with our recent changes we are now able to provide synchronous methods. This change will bring a more natural developer experience.

Ditto Initialization Required using await Ditto.init()

From 4.9.0 and later it’s required to call await Ditto.init() before using any Ditto functionality. Failing to do so can result in incorrect initialization of Ditto and unexpected failures.

Notably, this must happen after WidgetsFlutterBinding.ensureInitialized() has been called.

Flutter’s runApp already calls WidgetsFlutterBinding.ensureInitialized(), so if calling Ditto.init() inside the initState of a StatefulWidget, it will just work.

To initialize Ditto in main, use the following syntax:

Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Ditto.init();
    runApp(MyCoolApp());
}

Select asnyc functions have become sync

Select functions that don’t require asynchronous execution have been converted from async to standard sync operations.

Changed Methods Include:

  • ditto.startSync();
  • ditto.store.registerObserver(...);
  • ditto.sync.registerSubscription(...);

Example with basic replacement:

// Previous syntax
final observer = await ditto.store.registerObserver("SELECT * FROM tasks");

// New syntax
final observer = ditto.store.registerObserver("SELECT * FROM tasks");

Example chaining Future’s with .then() (often seen in initState()):

// Previous syntax
ditto.store.registerObserver("SELECT * FROM tasks").then((observer) {
  setState(() {
    _observer = observer;
  });
});

// New syntax
final observer = ditto.store.registerObserver("SELECT * FROM tasks");
setState(() {
    _observer = observer;
});

Methods to setter/getter variables

Async getter/setter method pairs have been converted to properties using Dart’s getter/setter syntax, for a more natural developer experience.

Changed Methods Include:

PreviousNew
DittoLogger.setMinimumLogLevel(...) DittoLogger.getMinimumLogLevel(...)DittoLogger.minimumLogLevel
DittoLogger.setCustomLogCallback(...) DittoLogger.getCustomLogCallback(...)DittoLogger.customLogCallback

Examples

// Previous syntax
await DittoLogger.setMinimumLogLevel(LogLevel.debug);
await DittoLogger.getMinimumLogLevel();

// New syntax
DittoLogger.minimumLogLevel = LogLevel.debug;
// Previous syntax
await DittoLogger.setCustomLogCallback((logLevel, logString) {
    print("$logLevel: $logString");
});

// New syntax
DittoLogger.customLogCallback = (logLevel, logString) {
    print("$logLevel: $logString");
}

Boolean functions renamed from getFoo/setFoo to isFoo

For property types where the value type is bool, the functions will have been renamed from getFoo and setFoo to isFoo.

Changed Methods Include:

PreviousNew
DittoLogger.setEnabled() DittoLogger.getEnabled()DittoLogger.isEnabled
ditto.setTransportConfig() ditto.getTransportConfig()ditto.transportConfig

Examples

// Previous syntax
await DittoLogger.setEnabled()
await DittoLogger.getEnabled()

// New syntax
DittoLogger.isEnabled
// Previous syntax
await ditto.setTransportConfig()
await ditto.getTransportConfig()

// New syntax
ditto.transportConfig

Select Async Initialization is now Sync

Objects with async constructors that didn’t require asynchronous initialization have been changed for a more natural developer experience.

Changed Methods Include:

  • All Identity subclass (E.g. OnlinePlaygroundIdentity)

Example:

// Previous syntax
final identity = await OnlinePlaygroundIdentity.create(
  appID: appID,
  token: token,
);

// New syntax
final identity = OnlinePlaygroundIdentity(
  appID: appID,
  token: token,
);

Restricted Class Modifiers

Many of our types are now either final classes (which prevent all subtyping), or interface classes (which allow implements but disallow other forms of subtyping).

If you were previously extending or implementing a type and this is now disallowed, you can create a wrapper type:

Example:

// Previous syntax
class MockDitto extends Mock implements Ditto {}

// New syntax
class DittoWrapper {
    final Ditto _ditto;
    // constructor

    Future<List<Task>> loadTasks() => ...;
}

class MockDitto extends Mock implements DittoWrapper {}

Some types may explicitly allow subtyping. Follow the API docs to understand when subtyping is best used.

Removal of dart:io types in public API

Directory and File are no longer found in our public API.

For example Ditto.open() previously took a Directory persistenceDirectory, but this has been changed to String.

This can be resolved by calling .path:

// Previous syntax
final persistenceDirectory = Directory("/my/cool/directory");
final ditto = await Ditto.open(
  persistenceDirectory: persistenceDirectory,
  // ...
);

// New syntax
final persistenceDirectory = Directory("/my/cool/directory");
final ditto = await Ditto.open(
  persistenceDirectory: persistenceDirectory.path,
  // ...
);

Min Kotlin Gradle Plugin Version Upgraded to 1.8.0

Supporting new Android capabilities requires using Kotlin Gradle Plugin version 1.8.0 or later.

To upgrade:

  1. Navigate to the root folder
  2. Open ./android/settings.gradle
  3. Update the org.jetbrains.kotlin.android plugin to 1.8.0
    plugins {
        // ...
        id "org.jetbrains.kotlin.android" version "1.8.0" apply false
    }
    
4.8.0-publicpreview.2
Release Date: Oct 25, 2024

Final Preview Release

Contains: Reliability and stability improvements.

4.8.0-publicpreview.1
Release Date: Oct 17, 2024

We’re excited to announce the public preview of the Ditto Flutter SDK!

While you can expect major feature compatibility with our production SDKs, we’re continuously refining the experience and closing any minor gaps. Join us on this journey, and share your feedback to help shape the future of Ditto for Flutter!

4.8.0-beta.12
Release Date: Oct 10, 2024

This change has a large number of new capabilities as well as some minor namings API Breaking Changes.

Added: Presence API - Allows receiving syncing information about other peers. See Using Mesh Presence

Added: WifiAware as a Transports protocol.

Added: Retrieving active subscriptions. Syncing Data > Retrieving Subscriptions

Added: Canceling subscriptions. Syncing Data > Canceling Subscriptions

Added: Support for sync groups. Creating Sync Groups

Added: Support to intercept and reject new connections. Handling Connection Requests

Breaking change: AuthenticationStatus.authenticated renamed to isAuthenticated

Breaking change: Various TransportConfig-related types have enabled properties renamed to isEnabled

Breaking change: HttpListenConfig.interfaceIp renamed to interfaceIP

Breaking change: HttpListenConfig.websocketSync renamed to webSocketSync

Breaking change: StoreObserver.cancelled renamed to isCancelled

4.8.0-beta.11
Release Date: Oct 4, 2024

Added: Logger class that provides diagnostics capabilities. Added: Device Dashboard Support - customers can now see device information on the Ditto Portal. Added: Support for Attachments.

Changed: The default deviceName will now represent the device’s model.

4.8.0-beta.10
Release Date: Sept 13, 2024

Added: OnlineWithAuthenticationIdentity and ditto.auth

Added: User-configurable transport config. Customizing Transports Configurations

4.8.0-beta.8 & 4.8.0-beta.9
No Notable Changes
4.8.0-beta.7
Release Date: Aug 26, 2024

Added: Ability to stop sync ditto.stopSync()

Fixed: Cancelling a subscription currently doesn’t trigger any action

Fixed: Observer API no longer fires every 50ms. Previously the observer API was using a polling mechanism internally which resulted in an event ever 50ms no matter if there were changes or not. This has been updated with an event listener pattern.

Changed: Performance and battery life improvements from observers not continuously firing

4.8.0-beta.6
Release Date: Aug 21, 2024

Version 4.8.0-beta.6 has been Retracted due to an issue found with Android observer API not firing correctly.

Please use earlier or later releases.

4.8.0-beta.5
Release Date: July 23, 2024

The initial beta release of Flutter