Dart

This documentation is for the Countly Dart SDK version 26.1.X. The SDK source code repository can be found here.

This SDK is written in Dart and is intended for any platform Dart can run on (including Android, iOS, web, Windows, macOS, and Linux). Runtime-specific logic is selected with conditional imports.

To examine the example integrations, please have a look here.

Adding the SDK to the Project

Add this to your project's pubspec.yaml file:

dependencies:
    countly_sdk_dart_core: ^26.1.0

After that, install dependencies from the command line:

dart pub get

The SDK requires Dart SDK version ^3.0.0.

SDK Integration

Minimal Setup

The shortest way to initialize the SDK is with the code below. If you don't provide a device ID, the SDK will generate one automatically.

import 'package:countly_sdk_dart_core/countly_sdk_dart_core.dart';

// Create the configuration with your app key and server URL
final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
);

// Initialize with that configuration
final sdk = await Countly.init(config);

Please check here for more information on how to acquire your application key (APP_KEY) and server URL.

A "CountlyConfig" object is used to configure the SDK during initialization. As shown above, you would create a "CountlyConfig" object and set the desired parameters before initializing the SDK.

Click here for more information about the "CountlyConfig" object functionalities.

If you are in doubt about the correctness of your Countly SDK integration you can learn about the verification methods from here.

Multi-Instance Support

This SDK supports running multiple SDK instances simultaneously. Each instance is identified by a unique instance key.

// Initialize the default instance
final defaultSdk = await Countly.init(config);

// Initialize a named instance
final secondSdk = await Countly.init(secondConfig, instanceKey: 'secondary');

// Access the default instance later
final sdk = Countly.defaultInstance;

// Access a named instance later
final sdk2 = Countly.instance('secondary');

To dispose of instances when they are no longer needed:

// Dispose a specific instance
await Countly.disposeInstance('secondary');

// Dispose all instances
await Countly.disposeAll();

SDK Data Storage

The SDK stores any unsent data persistently and will send it at the next available opportunity (next app start for example). By default the SDK uses in-memory storage. Persistent storage is used when enabled through configuration with a provided storage adapter or custom storage methods.

SDK Logging

If logging is enabled, the SDK will print out debug messages about its internal state and encountered problems.

We advise doing this while implementing Countly features in your application.

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  enableSDKLogs: true,
);

Log Levels

SDK debug log levels can be adjusted to reduce or increase the verbosity of the logs. Available levels are:

  • LogLevel.error - Only error messages
  • LogLevel.warning - Errors and warnings
  • LogLevel.info - Errors, warnings, and informational messages
  • LogLevel.debug - Detailed debug information
  • LogLevel.verbose (default) - All messages
final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  enableSDKLogs: true,
  logLevel: LogLevel.warning,
);

Custom Logger

The SDK accepts a custom logger which can be used to save logs to a desired location or format. You need to implement the SdkLogger abstract class:

class MyCustomLogger implements SdkLogger {
  @override
  bool isEnabled(LogLevel level) = true;

  @override
  void log(LogLevel level, String message, {Object? error, StackTrace? stack}) {
    // Your custom logging logic
    print('[$level] $message');
  }
}

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  logger: MyCustomLogger(),
);

Visual Warnings

The SDK can create UI warnings in the form of a toast for warning and error logs. This can be enabled during initialization:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  enableVisualWarnings: true,
);

Events

Event is any type of action that you can send to a Countly instance, e.g. purchase, settings changed, view enabled and so on.

All data passed to the Countly server via SDK or API should be in UTF-8.

In the SDK all event-related functionality can be accessed from the returned interface on:

Countly.defaultInstance?.events

When providing segmentation for events, the following data types are supported: "String," "int," "double," "bool," and "List". Please note that no other data types will be recorded.

Recording Events

The method signature is as follows:

Future<void> record({
  required String key,
  int? count,
  double? sum,
  double? dur,
  Map<String, dynamic>? segmentation,
})

You can record an event with all possible parameters like this:

Map<String, dynamic> segmentation = {
  'country': 'Germany',
  'app_version': '1.0',
  'rating': 10,
  'precision': 324.54678,
  'clicked': false,
  'languages': ['en', 'de', 'fr'],
};

await Countly.defaultInstance?.events.record(
  key: 'purchase',
  count: 1,
  sum: 0.99,
  dur: 60.0,
  segmentation: segmentation,
);

Device Metrics

The SDK will automatically gather device metric information during initialization and send this data to the server. Device metric information gathering is at 'best effort' level without any native code, only at Dart/Flutter level.

You can override device metric values during initialization:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  deviceMetricOverrides: {
    '_os': 'CustomOS',
    '_os_version': '1.0',
    '_device': 'CustomDevice',
  },
);

You can also record device metrics manually at any time:

// Record metrics with SDK-gathered values
await Countly.defaultInstance?.events.recordMetrics();

// Record metrics with custom overrides
await Countly.defaultInstance?.events.recordMetrics(
  metricOverride: {'_os': 'CustomOS'},
);

View Tracking

The SDK provides access to view-related functionality through the interface returned by:

Countly.defaultInstance?.views

Auto Stopped Views

The SDK offers a method to start a view which will be automatically stopped when this method is called again to start another view. View duration is tracked automatically.

// Start tracking a view - any previously active view will be automatically stopped
await Countly.defaultInstance?.views.startAutoStoppedView('Dashboard');

When a new view is started using startAutoStoppedView, the previously active view (if any) is automatically stopped and its duration is calculated and recorded.

Ending the Active View

If you need to manually end the currently active view without starting a new one, you can use:

await Countly.defaultInstance?.views.endActiveView();

View State Recovery

The SDK persists the active view state. If the app crashes or is terminated unexpectedly, the SDK will recover the active view on the next initialization and record its duration with a "recovered" flag in the segmentation.

Device ID Management

A device ID is a unique identifier for your users. You may specify the device ID or allow the SDK to generate it. When providing your device ID, ensure it is unique for all users. Potential sources for such an ID include the username, email, or other internal ID used by your other systems.

You may provide your custom device ID when initializing the SDK:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  deviceId: 'YOUR_DEVICE_ID',
);

Changing the Device ID

You may configure or change the device ID anytime using two approaches:

1. Change with merge (same user, new ID):

This will send the old device ID to the server so the user profile data can be merged under the new device ID. This is basically changing the current device ID of the user with a new one (with something you value more.)

await Countly.defaultInstance?.id.changeWithMerge('NEW_DEVICE_ID');

Performance risk. Changing device ID with server merging results in huge load on server as it is rewriting all the user history. This should be done only once per user.

2. Change without merge (new user):

This will flush any pending data under the old device ID, clear all queues, and start fresh as a new user. The SDK will also enter the unknown consent state and consent should be provided after.

await Countly.defaultInstance?.id.changeWithoutMerge('NEW_DEVICE_ID');

Retrieving Current Device ID

You may want to see what the current device ID is and its type. For that, you can use the following properties:

// Get the current device ID
String? currentDeviceId = Countly.defaultInstance?.deviceId;

// Get the device ID type (0 = provided, 1 = generated)
int? deviceIdType = Countly.defaultInstance?.deviceIdType;

Device ID Generation

When the SDK is initialized for the first time with no device ID, it will generate a device ID that is a random UUID.

User Profiles

Using the following calls, you can set key/value to the visitors user profile. After you send user data, it can be viewed under the User Profiles menu.

You would access Countly.defaultInstance?.users to see the available functionality for modifying user properties.

Setting User Profile Values During Init

If possible, set user properties during initialization. This way they would be sent shortly after the SDK initialization.

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  userProperties: {
    'name': 'Nicola Tesla',
    'username': 'nicola',
    'email': 'info@nicola.tesla',
    'tier': 'premium',
  },
);

Setting User Profile Values

The following calls can be used after init.

You can set multiple properties at the same time using:

Map<String, dynamic> userProperties = {
  'name': 'Nicola Tesla',
  'username': 'nicola',
  'email': 'info@nicola.tesla',
  'organization': 'Trust Electric Ltd',
  'phone': '+90 822 140 2546',
  'picture': 'http://example.com/picture.jpg',
  'gender': 'M',
  'byear': '1919',
  'special_value': 'something special',
};
await Countly.defaultInstance?.users.setProperties(userProperties);

The SDK provides named constants for predefined user property keys:

// Available named user properties:
NamedUserProperty.name        // 'name'
NamedUserProperty.username    // 'username'
NamedUserProperty.email       // 'email'
NamedUserProperty.organization // 'organization'
NamedUserProperty.phone       // 'phone'
NamedUserProperty.picture     // 'picture'
NamedUserProperty.gender      // 'gender'
NamedUserProperty.byear       // 'byear'

The SDK caches set properties, bundles them and sends to the server when necessary with a minimum amount of network requests.

Modifying Custom Data

Additionally, you can do different manipulations on your custom data values, like storing an array of values under the same property.

Below is the list of available array operations:

// Add values to array (allows duplicates)
await Countly.defaultInstance?.users.pushToArray('interests', ['reading', 'coding']);

// Add unique values to array (no duplicates)
await Countly.defaultInstance?.users.addToSet('tags', ['premium', 'active']);

// Remove values from array
await Countly.defaultInstance?.users.pullFromArray('tags', ['inactive']);

User Consent

For compatibility with data protection regulations, such as GDPR, the Countly Dart SDK allows developers to enable/disable data collection depending on user consent. More information about GDPR can be found here.

Consent in this SDK is all-or-nothing, singe consent is given to enable or disable all data collection.

Setup During Init

By default, the SDK requires consent. To give consent during initialization:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  giveConsent: true,
);

Unknown Consent Mode

The SDK offers an 'unknown consent mode' during initialization. This mode will keep collecting data as normal but will not send it to the server until consent is given. If consent is revoked, all collected data will be erased. If consent is given, collected data will be processed.

Data collected during this mode will be in memory only (so if the app is closed it will be lost). In this mode, the SDK can still send previously saved data from prior app usage.

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  startWithUnknownConsent: true,
);

SDK Behavior Setting, Health Check and Consent Report requests are excluded from consent, meaning they will be sent to the server regardless of consent state.

Changing Consent

The end-user can change their mind about consent at a later time. To reflect these changes in the Countly SDK, you can use the following methods:

// Give consent - enables all data collection
await Countly.defaultInstance?.consents.giveConsent();

// Revoke consent - stops all tracking and clears queued data
await Countly.defaultInstance?.consents.revokeConsent();

Security and Privacy

HTTP Method

This SDK uses only POST requests for all server communication.

Setting Custom Network Request Headers

If you need to include custom network request headers in the requests sent by the SDK, you can add them during initialization:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  customRequestHeaders: {
    'X-Custom-Header': 'customValue',
  },
);

Other Features and Notes

SDK Config Parameters Explained

Here is the list of all parameters "CountlyConfig" provides:

  • deviceId - A custom device ID. If not provided, the SDK generates a random UUID.
  • enableSDKLogs - Enable SDK internal debugging logs.
  • logLevel - Set the verbosity of SDK logs (error, warning, info, debug, verbose).
  • logger - Provide a custom logger implementation.
  • enableVisualWarnings - Show toast warnings for warning and error logs.
  • giveConsent - Give consent for data collection at initialization.
  • startWithUnknownConsent - Start in unknown consent mode, collecting data in memory until consent is given.
  • userProperties - Set initial user properties during initialization.
  • deviceMetricOverrides - Override automatically gathered device metric values.
  • customRequestHeaders - Set custom HTTP headers to be sent with each request.
  • sbs - Provide initial server behavior settings.
  • disableOldDataMigration - Skip Flutter SDK data migration when set to true if you are migrating from it.

Example Integrations

Below you can see steps to download and run the Countly Dart SDK example application. It assumes Dart is installed in your system:

# clone the Countly SDK repository
git clone https://github.com/Countly/countly-sdk-dart.git

# dive into the example directory
cd countly-sdk-dart/example

# install packages and run
dart pub get
dart run main.dart

This example application has most of the methods mentioned in this documentation, and it is an excellent way to understand how different methods work, like events, views, user profiles, consent, and device ID management.

Server Configuration

The SDK supports Server Behavior Settings (SBS) which allows you to modify SDK behavior from the server without releasing a new app or SDK update.

You can provide initial SBS values during initialization. If not provided, the SDK will fetch them from the server automatically:

final config = CountlyConfig(
  appKey: APP_KEY,
  serverUrl: SERVER_URL,
  sbs: {'eqs': 50, 'rqs': 500},
);

Through SBS, you can blacklist or whitelist (mutually exclusive) recording of:

  • Events according to event key
  • User properties according to the property key
  • Event segmentation according to segmentation key
  • Specific segmentation of an event according to the event and segmentation keys

These are enforced at the SDK level.

SDK Internal Limits

Countly SDK has internal limits to prevent users from unintentionally sending large amounts of data to the server. If these limits are exceeded, the data will be truncated to keep it within the limit.

The default internal limits (configurable through SBS) are:

  • Key Length (lkl) - Maximum size of all user set keys: 128 characters
  • Value Size (lvs) - Maximum size of string segmentation values: 256 characters
  • Segmentation Values (lsv) - Maximum number of segmentation key-value pairs: 100 entries
  • Event Queue Size (eqs) - Maximum events held before flush: 100
  • Request Queue Size (rqs) - Maximum requests held: 1000
  • User Properties Cache (upcl) - Maximum cached properties before flush: 100

Caches and Queues

The SDK has several caches and queues to optimize network usage:

  • User Property Cache - Prevents sending each state of frequently changed properties to reduce request count.
  • Event Queue - Bundles events into a single request to reduce request count.
  • Request Queue - Minimizes data loss due to internet connectivity or server downtime.

These queues have limits that can be configured through SBS. Reaching the limit causes early bundling for the User Property cache and Event queue, but for the Request queue it means the oldest data will be removed to accommodate new ones.

Checking if the SDK has been initialized

In case you would like to check if the SDK has been initialized and is ready to use, you may check:

// Check if an instance exists and is not disposed
final sdk = Countly.defaultInstance;
if (sdk != null && !sdk.isDisposed) {
  // SDK is initialized and ready
}

Manually Flushing the Queue

If you want to manually trigger sending all pending events and requests to the server, you can use:

await Countly.defaultInstance?.processEventsAndRequests();

FAQ

What Information is Collected by the SDK?

The data that SDKs gather to carry out their tasks and implement the necessary functionalities is mentioned in here.

What Platforms are Supported?

This Dart SDK is written in pure Dart and supports the runtimes it explicitly implements via conditional imports, including Android, iOS, web, Windows, macOS, and Linux.

Was this page helpful?
Reach out to us for any other questions.
Helpful?

Looking for more Help?