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.
Overview
This guide covers the essential changes needed to migrate your Ditto Flutter app from v4 to v5. The main architectural shift is moving from identity-based initialization toDittoConfig, plus several breaking type changes.
Also required: v5 uses DQL (Ditto Query Language) for all data operations. See the DQL Migration Guide for query migration steps.
AI Agent Prompt
Use this prompt when working with an AI coding assistant to migrate your Ditto Flutter app from v4 to v5.Copy AI Migration Prompt (Click to Expand)
Copy AI Migration Prompt (Click to Expand)
Ditto Instance Initialization
v5 separates initialization into four distinct phases for better clarity and control. Note thatDitto.init() is a prerequisite step that must be called before any Ditto usage:
- Clear separation of connection, initialization, and authentication concerns
- Async initialization prevents blocking the main thread
- No workarounds needed (transport config, DQL strict mode, v3 sync disable)
- Type-safe configuration with compile-time validation
Replace Initialization
Replace Identity β DittoConfig mapping:
Key changes:
Ditto.open(identity:) with Ditto.open(DittoConfig).| v4.14 Identity | v5.0 DittoConfig connect |
|---|---|
OnlinePlaygroundIdentity(appID:token:customAuthUrl:) | DittoConfigConnectServer(url:) |
OnlineWithAuthenticationIdentity(appID:customAuthUrl:) | DittoConfigConnectServer(url:) + auth handler |
OfflinePlaygroundIdentity(appID:siteID:) | DittoConfigConnectSmallPeersOnly(privateKey: null) |
SharedKeyIdentity(appID:sharedKey:siteID:) | DittoConfigConnectSmallPeersOnly(privateKey: key) |
- Use
DittoConfigwithdatabaseIDinstead of identity types appIDβdatabaseID- Connect mode selected via
DittoConfigConnect*subclasses DittoConfigConnectServer.urltakes aString, not aUri- New:
Ditto.openSync(config)synchronous alternative ditto.sync.start()instead ofditto.startSync()
Update Authentication
Set authentication handler after initialization.Key changes:
AuthenticationHandlerinterface removed β useAuthenticationExpirationHandlertypedef- Set handler via
await ditto.auth.setExpirationHandler(...)(async method, not property assignment) ditto.authis non-nullable in v5 (no?.needed)- Handler signature:
void Function(Ditto ditto, Duration timeUntilExpiration)β receivesDittoinstance andDuration, notAuthenticatorandint Authenticator.loginWithCredentials(username:password:provider:)removed β use token-basedlogin(token:provider:)Authenticator.developmentProvideris the dev provider constant
Additional Changes
DQL Strict Mode Behavior Change
Choose the appropriate migration path based on your current v4 configuration:Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
If youβre currently using the v4 default (
DQL_STRICT_MODE=true), you must explicitly set strict mode to true in v5 before starting sync or executing any queries.To migrate to
DQL_STRICT_MODE=false (the new v5 default), contact Ditto Customer Support for guidance.Currently Using DQL with DQL_STRICT_MODE=false
Currently Using DQL with DQL_STRICT_MODE=false
If you explicitly set
DQL_STRICT_MODE=false in v4, no changes are required.v5 uses DQL_STRICT_MODE=false as the default, so your existing DQL queries will behave identically. You can upgrade freely.Optional: You can remove the explicit ALTER SYSTEM SET DQL_STRICT_MODE = false statement in v5 since this is now the default behavior.Currently Using Legacy Query Builder
Currently Using Legacy Query Builder
The Legacy Query Builder has been removed in v5. All queries must be converted to DQL before upgrading.Good news: Legacy Query Builder functionality has 1:1 support with
DQL_STRICT_MODE=false (the v5 default), making migrations straightforward.Migration Steps:-
In v4: Set
DQL_STRICT_MODE=falseafter initialization: - Convert all queries from Legacy Query Builder to DQL See the Flutter LegacyβDQL Migration Guide for detailed conversion examples.
-
Upgrade to v5
No DQL configuration changes requiredβv5 defaults to
DQL_STRICT_MODE=false.
Default Persistence Directory
v5 includes the database ID in the default directory name:ditto-{databaseID} instead of ditto. This is for only new databases created in v5. v5 does not migrate existing databases to the new structure.
Provide custom persistence directory:
Observer Changes
v4 and v5 both use callback-based observers. The coreregisterObserver API pattern is the same in both versions, returning a StoreObserver that you retain and call .cancel() on for cleanup.
- The observer lifecycle pattern (retain,
.cancel(), cleanup) is the same as v4 - v5 observers expose a
Stream<QueryResult> changesproperty for reactive use StoreObserver.queryStringandqueryArgumentsproperties available for introspection
Back Pressure
The Flutter SDK handles back pressure internally through itsStream<QueryResult>-based observer model. There is no signalNext function to call β the SDK manages update delivery automatically.
StreamController with pause / resume on the observerβs stream.
PresenceGraph.remotePeers Type Changed
WebSocket Sync Default Changed
TransportConfig Now Immutable
TransportConfig and all its sub-types are now @immutable. Use the builder pattern or the updateTransportConfig convenience method:
Isolate Restriction
Ditto and Store are marked @pragma("vm:isolate-unsendable"). They cannot be passed across Dart isolates.
API Renames
| v4.14 | v5.0 |
|---|---|
ditto.startSync() | ditto.sync.start() |
ditto.stopSync() | ditto.sync.stop() |
ditto.isSyncActive | ditto.sync.isActive |
Peer.peerKeyString + Peer.peerKey: Uint8List | Peer.peerKey: String (unified) |
Peer.isConnectedToDittoCloud | Peer.isConnectedToDittoServer |
Connection.peerKeyString1 / peerKeyString2 | Connection.peer1 / Connection.peer2 |
ConnectionRequest.peerKeyString | ConnectionRequest.peerKey |
ditto.persistenceDirectory | ditto.absolutePersistenceDirectory |
SmallPeerInfo.syncScope: SmallPeerInfoSyncScope | DQL: ALTER SYSTEM SET sync_scope = '...' |
SmallPeerInfoSyncScope enum | Removed β use DQL |
AuthenticationHandler interface | AuthenticationExpirationHandler typedef |
Authenticator.loginWithCredentials(username:password:provider:) | Removed β use token-based login(token:provider:) |
TransportConfig.withBigPeer(...) | Removed β use DittoConfig server configuration |
DittoLogger.setLogFile(...) | Removed β file logging no longer supported |
HttpListenConfig.staticContentPath | Removed β no replacement |
PresenceGraph.remotePeers: List<Peer> | PresenceGraph.remotePeers: Set<Peer> |
SiteID class | Not exported (internal type) |
DocumentID class | Not exported (internal type) |
Key Initialization Changes
| v4.14 | v5.0 | Notes |
|---|---|---|
Ditto.open(identity: Identity) | Ditto.open(DittoConfig) | Positional parameter, DittoConfig has default |
Identity subclasses | DittoConfigConnect* subclasses | See identity mapping table |
OnlinePlaygroundIdentity.customAuthUrl: String? | DittoConfigConnectServer(url: String) | URL is now a String, not Uri |
OnlinePlaygroundIdentity.enableDittoCloudSync | Removed | Cloud sync is configured via connect mode |
Authentication API Changes
| v4.14 | v5.0 | Notes |
|---|---|---|
AuthenticationHandler interface | AuthenticationExpirationHandler typedef | void Function(Ditto, Duration) |
authenticationRequired(Authenticator) | expirationHandler called with Duration.zero | Single handler replaces two callbacks |
authenticationExpiringSoon(Authenticator, int) | expirationHandler called with remaining Duration | Combined into one handler |
authenticator.loginWithCredentials(username:password:provider:) | Removed | Use login(token:provider:) |
authenticator.login(token:provider:) | ditto.auth.login(token:provider:) | Same API, accessed from Ditto.auth |
| Set handler via identity constructor | await ditto.auth.setExpirationHandler(...) | Async method, set after Ditto.open() |
APIs Removed Without Replacement
| Removed API | Notes |
|---|---|
Connection.approximateDistanceInMeters | Peer distance estimation removed |
DittoLogger.setLogFile(...) | File logging removed entirely |
HttpListenConfig.staticContentPath | Static HTTP file serving removed |
TransportConfig.withBigPeer(...) | Use DittoConfig server config instead |
SmallPeerInfoSyncScope enum | Sync scope is now a DQL ALTER SYSTEM setting |
ManualIdentity | Manual certificate identity removed |
New APIs in v5
Ditto.init()β required static initialization before using any Ditto features (throws if not called)DittoConfig/DittoConfigConnectServer/DittoConfigConnectSmallPeersOnlyβ new configuration hierarchy replacing all identity typesDitto.openSync(config)β synchronous constructor alternative to asyncDitto.open(config)Ditto.defaultRootDirectoryβ static helper for default persistence pathditto.configβ access the config object used to open the instanceAuthenticationExpirationHandlertypedef β replacesAuthenticationHandlerinterfaceAuthenticator.developmentProviderβ constant for development auth providerAuthenticator.setExpirationHandler()β async method to set the expiration handlerSyncSubscription.queryArgumentsCborBytes/queryArgumentsJsonStringβ inspect subscription query arguments- Builder types for all transport config sub-types (
TransportConfigBuilder,PeerToPeerBuilder, etc.) TransportConfig.withAllPeerToPeerEnabled(bool)β immutable copy method for enabling P2P transportsDittoandStoremarked@pragma("vm:isolate-unsendable")for safety
Migration Checklist
Initialization
- Ensure
await Ditto.init()is called before any Ditto usage - Replace
Ditto.open(identity:)withDitto.open(DittoConfig(...)) - Create
DittoConfigwithdatabaseIDandconnectmode - Update identity types to
DittoConfigConnect*variants - Use
StringURLs (notUri) forDittoConfigConnectServer - Remove
updateTransportConfigcalls for WebSocket URLs (cloud URL inferred from config) - Set
DQL_STRICT_MODE=trueBEFOREsync.start()if maintaining v4 behavior - Update
startSync()βsync.start()
Authentication
- Replace
AuthenticationHandlerinterface withAuthenticationExpirationHandlertypedef - Set handler via
await ditto.auth.setExpirationHandler(...)afterDitto.open() - Update handler signature: receives
(Ditto ditto, Duration timeUntilExpiration), not(Authenticator, int) - Replace
loginWithCredentials(username:password:provider:)withlogin(token:provider:) - Use
Authenticator.developmentProviderfor dev tokens - Remove
?.onditto.authβ it is non-nullable in v5
Breaking Changes
- Update
remotePeers[0]βremotePeers.first(type changed fromListtoSet) - Check
HttpListenConfig.websocketSyncβ must be explicitly enabled now (default isfalse) - Update
TransportConfigusage to builder pattern:.toBuilder()...build()orupdateTransportConfig() - Update
peerKeyStringβpeerKey - Update
isConnectedToDittoCloudβisConnectedToDittoServer - Update
persistenceDirectoryβabsolutePersistenceDirectory - Do not pass
DittoorStoreacross isolates - Set
persistenceDirectoryinDittoConfigif maintaining v4 directory structure
Verification
- Build compiles with zero errors
- Observers update data immediately
- Authentication works before sync starts
- No
remotePeersindex access errors - WebSocket sync works if needed (explicitly enabled)