Docs Menu
Docs Home
/ /
Atlas Device SDKs
/ /

Manage a Sync Session - Flutter SDK

On this page

  • Prerequisites
  • Wait for Changes to Upload and Download
  • Pause and Resume a Sync Session
  • When to Pause a Sync Session
  • Monitor Sync Upload and Download Progress
  • Monitor Network Connection
  • Manually Reconnect All Sync Sessions

When you use Atlas Device Sync, the Flutter SDK syncs data with Atlas in the background using a sync session. The sync session starts whenever you open a synced realm.

The sync session manages the following:

  • Uploading and downloading changes to the synced database

  • Pausing and resuming sync

  • Monitoring sync progress

  • Monitoring network connectivity

You can access the Session of any synced realm through the Realm.syncSession property.

Before you can manage your sync session state, you must perform the following:

  1. Configure Flexible Sync on the Atlas App Services backend

  2. Add Device Sync to your app

To asynchronously wait for all changes to upload to Atlas from your synced realm, call Session.waitForUpload(). To asynchronously wait for all changes on Atlas to download to your synced realm, call Session.waitForDownload().

// Wait to download all pending changes from Atlas
await realm.syncSession.waitForDownload();
// Add data locally
realm.write(() {
realm.addAll<Car>([
Car(ObjectId(), "Hyundai"),
Car(ObjectId(), "Kia"),
Car(ObjectId(), "Lincoln")
]);
});
// Wait for changes to upload to Atlas before continuing execution.
await realm.syncSession.waitForUpload();

You can add an optional CancellationToken to waitForUpload() and waitForDownload().

final cancellationToken = CancellationToken();
final waitForDownloadFuture =
realm.syncSession.waitForDownload(cancellationToken);
cancellationToken.cancel();
final waitForUploadFuture =
realm.syncSession.waitForUpload(cancellationToken);
cancellationToken.cancel();

To pause syncing for a session, call Session.pause(). The realm will not sync changes with Atlas while the session is paused.

To resume syncing a changes, call Session.resume().

You must manually call Session.pause() and Session.resume() for each realm whose Sync session you want to pause and restart. The sync state of one session has no impact on other open sessions.

The following code block demonstrates calling these methods:

// Pause the sync session
realm.syncSession.pause();
// Data that you add while the sync session is paused does not sync to Atlas.
// However, the data is still added to the realm locally.
realm.write(() {
realm.addAll<Car>([
Car(ObjectId(), "Volvo"),
Car(ObjectId(), "Genesis"),
Car(ObjectId(), "VW")
]);
});
// Resume sync session. Now, the data you wrote to the realm
// syncs to Atlas.
realm.syncSession.resume();

For most applications, there is no need to manually pause and resume a sync session. However, there are a few circumstances under which you may want to pause or suspend a sync session:

  • You only want to sync after the user takes a specific action

  • You only want to sync during a certain time of the day

  • You don't want to attempt to sync when there is poor network connectivity

  • You want to explicitly force a sync session to connect

In the case of poor network connectivity, continually trying to establish a network connection can drain the user's device battery.

The case of explicitly forcing a sync session to connect is most commonly related to being offline for some time. The sync client attempts to connect, and upon failure, goes into exponential backoff. After being offline for a long time, the client may not immediately reconnect. Pausing and resuming the sync session explicitly forces the connection.

When you do pause a sync session, keep these things in mind:

  • If the client may be offline longer than the client maximum offline time, the client will be unable to resume syncing and must perform a client reset.

  • Pausing a sync session pauses it in both directions. Changes that your app makes on the device do not sync with the backend, and changes to the data in the backend or on other devices do not sync to the device. There is no way to pause only uploads or pause only downloads.

  • Do not pause a sync session if you want a client to permanently stop syncing with the backend. To permanently stop syncing, copy the contents of the synced realm into a non-synced realm, and use the non-synced realm in the client.

Do not pause sync to stop syncing for indefinite time periods or time ranges in months and years. The functionality is not designed or tested for these use cases. You could encounter a range of issues when using it this way.

Changed in version 2.0.0: transferredBytes and transferrableBytes deprecated in favor of progressEstimate

To monitor Sync progress, call SyncSession.getProgressStream(). This method returns a Stream of SyncProgress objects that provide a progressEstimate for the current upload or download.

The provided progressEstimate is a double whose value ranges from 0.0 to 1.0. At 1.0, the progress stream is complete.

SyncSession.getProgressStream() takes two arguments:

  • A ProgressDirection enum that can be set to upload or download. Specifies whether the progress stream monitors upload or download progress.

  • A ProgressMode enum that can be set to one of the following:

    • reportIndefinitely: Sets notifications to continue until the callback is unregistered.

    • forCurrentlyOutstandingWork: Sets notifications to continue until the progressEstimate reaches 1.0.

final stream = realm.syncSession.getProgressStream(
ProgressDirection.upload, ProgressMode.forCurrentlyOutstandingWork);
double progressEstimate = -1;
late StreamSubscription streamListener;
streamListener = stream.listen((syncProgressEvent) {
progressEstimate = syncProgressEvent.progressEstimate;
if (progressEstimate < 1.0) {
print('Upload progress: ${progressEstimate * 100}%');
}
}, onDone: () {
print('Upload progress: ${progressEstimate * 100}%');
print("Upload complete");
}, onError: (error) {
print("An error occurred: $error");
streamListener.cancel();
});

Tip

You can also configure sync monitoring by using the onProgressCallback when you first open a Realm.

You can get the state of the current network connection with Session.connectionState. This returns a ConnectionState enum that contains the network connection's state: connected, disconnected, or connecting.

if (realm.syncSession.connectionState == ConnectionState.connected) {
// ... do stuff
}

Monitor the state of the network connection with Session.connectionStateChanges. This property returns a Stream of ConnectionStateChange objects that updates when the network connection changes. You can access the current and previous ConnectionState from ConnectionStateChange.

final connectionStream = realm.syncSession.connectionStateChanges;
late StreamSubscription streamListener;
streamListener = connectionStream.listen((connectionStateChange) {
if (connectionStateChange.current == ConnectionState.connected) {
print("Connected to Atlas Device Sync server");
streamListener.cancel();
}
});

The Flutter SDK automatically detects when a device regains connectivity after being offline and attempts to reconnect using an incremental backoff strategy.

You can choose to manually trigger a reconnect attempt with the App.reconnect() instead of waiting for the duration of the incremental backoff. This is useful if you have a more accurate understanding of the network conditions and don't want to rely on automatic reconnect detection.

app.reconnect();

When you call this method, the SDK forces all sync sessions to attempt to reconnect immediately and resets any timers used for incremental backoff.

Important

Cannot Reconnect Within Socket Read Timeout Duration

The Flutter SDK has an internal default socket read timeout of 2 minutes, where the SDK will time out if a read operation does not receive any data within a 2-minute window. If you call App.Sync.reconnect() within that window, the Flutter SDK does not attempt to reconnect.

Back

Write Data to a Synced Realm