This documentation applies to the version 20.4.
This document includes necessary information for integrating Countly Flutter SDK in your application. Flutter SDK requires Android and iOS SDKs, hence all the features and limitations regarding those platforms also apply to Countly Flutter SDK.
Installing the SDK
Add this to your package's pubspec.yaml
file:
dependencies:
countly_flutter:
git:
url: https://github.com/Countly/countly-sdk-flutter-bridge.git
ref: master
You can install packages from the command line with Flutter:
flutter pub get
Example Flutter application
Below you can see steps to download a Flutter example application:
git clone https://github.com/Countly/countly-sdk-flutter-bridge.git
cd countly-sdk-flutter-bridge/example
flutter pub get
flutter run
This example application has all the methods mentioned in this documentation. It is a great way of understanding how different methods work, like custom events, custom user profiles and views.
Implementation
Below you can find necessary code snippets to initialize the SDK for sending data to Countly servers. Where possible, use your server URL instead of try.count.ly
in case you have your own server.
Initialization
The shortest way to initiate the SDK if you want Countly SDK to take care of device ID seamlessly, use the code below. You can find your app key on your Countly dashboard, under the "Applications" menu item.
// use your server name below if required.
Countly.isInitialized().then((bool isInitialized){
if(!isInitialized){
// Features which is required before init should be call here
Countly.init("https://try.count.ly", APP_KEY).then((value){
//Features dependent on init should be set here, for e.g Push notifications and consent.
});
}else{
print("Countly: Already initialized.");
}
});
Enabling logging
If logging is enabled then our SDK will print out debug messages about its internal state and encountered problems.
We advise doing this while implementing Countly features in your application.
Countly.setLoggingEnabled(true);
Checking if the SDK has been initialized
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
In case you would like to check if init has been called, you may use the following function:
Countly.isInitialized();
Device ID
When the SDK is initialized for the first time and no device ID is provided, a device ID will be generated by SDK.
For iOS: the device ID generated by SDK is the Identifier For Vendor (IDFV)
For Android: the device ID generated by SDK is the OpenUDID or Google Advertising ID
You may provide your own custom device ID when initializing the SDK
Countly.init(SERVER_URL, APP_KEY, DEVICE_ID)
Changing the Device ID
You may configure/change the device ID anytime using:
Countly.changeDeviceId(DEVICE_ID, ON_SERVER);
You may either allow the device to be counted as a new device or merge existing data on the server. If theonServer
bool is set to true
, the old device ID on the server will be replaced with the new one, and data associated with the old device ID will be merged automatically.
Otherwise, if onServer
bool is set to false
, the device will be counted as a new device on the server.
Temporary Device ID
You may use a temporary device ID mode for keeping all requests on hold until the real device ID is set later.
You can enable temporary device ID when initializing the SDK:
Countly.init(SERVER_URL, APP_KEY, Countly.deviceIDType["TemporaryDeviceID"])
To enable a temporary device ID after init, you would call:
Countly.changeDeviceId(Countly.deviceIDType["TemporaryDeviceID"], ON_SERVER);
Note: When passing TemporaryDeviceID
for deviceID
parameter, argument for onServer
parameter does not matter.
As long as the device ID value is TemporaryDeviceID
, the SDK will be in temporary device ID mode and all requests will be on hold, but they will be persistently stored.
When in temporary device ID mode, method calls for presenting feedback widgets and updating remote config will be ignored.
Later, when the real device ID is set using Countly.changeDeviceId(DEVICE_ID, ON_SERVER);
method, all requests which have been kept on hold until that point will start with the real device ID
Parameter tampering protection
You can set optional salt
to be used for calculating checksum of request data, which will be sent with each request using &checksum
field. You need to set exactly the same salt
on the Countly server. If salt
on Countly server is set, all requests would be checked for the validity of &checksum
field before being processed.
// sending data with salt
Countly.enableParameterTamperingProtection("salt");
Make sure not to use salt on the Countly server and not on the SDK side, otherwise, Countly won't accept any incoming requests.
Optional parameters during initialization
You can provide optional parameters that will be used during begin_session request. They must be set right after the init
function so that they are set before the request is sent to the server. To set them, use the setOptionalParametersForInitialization
function. If you want to set those optional parameters, this function has to be called every time the app starts. If you don't want to set one off those values, leave that field null
.
The optional parameters are:
- Country code: ISO Country code for the user's country
- City: Name of the user's city
- Location: Comma separate latitude and longitude values, for example "56.42345,123.45325"
- Your user’s IP address
//setting optional parameters
Map<String, Object> options = {
"city": "Tampa",
"country": "US",
"latitude": "28.006324",
"longitude": "-82.7166183",
"ipAddress": "255.255.255.255"
};
Countly.setOptionalParametersForInitialization(options);
//and then call the below code
Countly.init(this, "https://YOUR_SERVER", "YOUR_APP_KEY", "YOUR_DEVICE_ID")
Forcing HTTP POST
If the data sent to the server is short enough, the SDK will use HTTP GET requests. In case you want an override so that HTTP POST is used in all cases, call the setHttpPostForced
function after you called init
. You can use the same function later in the app's life cycle to disable the override. This function has to be called every time the app starts.
Countly.setHttpPostForced(true); // default is false
Session control
To start recording a session you would start it with:
Countly.start();
If you want to end recording the current session, you would call:
Countly.stop();
Recording an event
A custom event is any type of action that you can send to a Countly instance, e.g purchase, settings changed, view enabled and so. This way it's possible to get much more information from your application compared to what is sent from Flutter SDK to Countly instance by default.
Data passed should be in UTF-8
All data passed to the Countly server via SDK or API should be in UTF-8.
As an example, we will be recording a purchase event. Here is a quick summary of what information each usage will provide us:
- Usage 1: how many times a purchase event occurred.
- Usage 2: how many times purchase event occurred + the total amount of those purchases.
- Usage 3: how many times purchase event occurred + which countries and application versions those purchases were made from.
- Usage 4: how many times a purchase event occurred + the total amount both of which are also available segmented into countries and application versions.
- Usage 5: how many times purchase event occurred + the total amount both of which are also available segmented into countries and application versions + the total duration of those events (under Timed Events topic below)
1. Event key and count
// example for sending basic custom event
var event = {
"key": "Basic Event",
"count": 1
};
Countly.recordEvent(event);
2. Event key, count and sum
// example for event with sum
var event = {
"key": "Event With Sum",
"count": 1,
"sum": "0.99",
};
Countly.recordEvent(event);
3. Event key and count with segmentation(s)
// example for event with segment
var event = {
"key": "Event With Segment",
"count": 1
};
event["segmentation"] = {
"Country": "Germany",
"Age": "28"
};
Countly.recordEvent(event);
4. Event key, count and sum with segmentation(s)
// example for event with segment and sum
var event = {
"key": "Event With Sum And Segment",
"count": 1,
"sum": "0.99"
};
event["segmentation"] = {
"Country": "Germany",
"Age": "28"
};
Countly.recordEvent(event);
Timed events
1.Timed event with key
// Basic event
Countly.startEvent("Timed Event");
Timer timer;
timer = new Timer(new Duration(seconds: 5), () {
Countly.endEvent({ "key": "Timed Event" });
timer.cancel();
});
2.Timed event with key and sum
// Event with sum
Countly.startEvent("Timed Event With Sum");
Timer timer;
timer = new Timer(new Duration(seconds: 5), () {
Countly.endEvent({ "key": "Timed Event With Sum", "sum": "0.99" });
timer.cancel();
});
3.Timed event with key, count and segmentation
// Event with segment
Countly.startEvent("Timed Event With Segment");
Timer timer;
timer = new Timer(new Duration(seconds: 5), () {
var event = {
"key": "Timed Event With Segment",
"count": 1,
};
event["segmentation"] = {
"Country": "Germany",
"Age": "28"
};
Countly.endEvent(event);
timer.cancel();
});
4.Timed event with key, count, sum and segmentation
// Event with Segment, sum and count
Countly.startEvent("Timed Event With Segment, Sum and Count");
Timer timer;
timer = new Timer(new Duration(seconds: 5), () {
var event = {
"key": "Timed Event With Segment, Sum and Count",
"count": 1,
"sum": "0.99"
};
event["segmentation"] = {
"Country": "Germany",
"Age": "28"
};
Countly.endEvent(event);
timer.cancel();
});
User Profiles
In order to set a user profile, use the code snippet below. After you send user data, it can be viewed under the User Profiles menu.
Note that this feature is available only for Enterprise Edition.
// example for setting user data
Map<String, Object> options = {
"name": "Nicola Tesla",
"username": "nicola",
"email": "info@nicola.tesla",
"organization": "Trust Electric Ltd",
"phone": "+90 822 140 2546",
"picture": "http://images2.fanpop.com/images/photos/3300000/Nikola-Tesla-nikola-tesla-3365940-600-738.jpg",
"picturePath": "",
"gender": "M", // "F"
"byear": "1919",
};
Countly.setUserData(options);
In order to modify a user's data (e.g increment, etc), the following code sample can be used.
Modifying custom data
Additionally, you can do different manipulations on your custom data values, like increment current value on the server or store an array of values under the same property.
Below is the list of available methods:
//set one custom properties
Countly.setProperty("setProperty", "My Property");
//increment used value by 1
Countly.increment("increment");
//increment used value by provided value
Countly.incrementBy("incrementBy", 10);
//multiply value by provided value
Countly.multiply("multiply", 20);
//save maximal value
Countly.saveMax("saveMax", 100);
//save minimal value
Countly.saveMin("saveMin", 50);
//set value if it does not exist
Countly.setOnce("setOnce", 200);
//insert value to array of unique values
Countly.pushUniqueValue("type", "morning");;
//insert value to array which can have duplocates
Countly.pushValue("type", "morning");
//remove value from array
Countly.pullValue("type", "morning");
Crash reporting
This feature allows the Countly SDK to record crash reports of either encountered issues of exceptions which cause your application to crash. Those reports will be sent to your Countly server for further inspection.
If a crash report can not be delivered to the server (e.g. no internet connection, unavailable server), then SDK stores the crash report locally in order to try again later.
Enabling crash reporting
If you want to enable automatic unhandled crash reporting, you need to call this before init:
Countly.enableCrashReporting()
By doing that it will automatically catch all errors that are thrown from within the Flutter framework.
If you want to catch Dart errors, run your app inside a Zone and supply Countly.recordDartError
to the onError
parameter:
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
void main() {
runZonedGuarded<Future<void>>(() async {
runApp(MyApp());
}, Countly.recordDartError);
}
Adding a custom key-value segment to a crash report
You may add a key/value segment to crash reports. For example, you could set which specific library or framework version you used in your app. You may then figure out if there is any correlation between the specific library or another segment and the crash reports.
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
The following call will add the provided segmentation to all recorded crashes. Use the following function for this purpose:
Countly.setCustomCrashSegment(Map<String, Object> segments);
Adding breadcrumbs
Throughout your app, you can leave crash breadcrumbs which would describe previous steps that were taken in your app before the crash. After a crash happens, they will be sent together with the crash report.
Following the command adds crash breadcrumb:
Countly.addCrashLog(String logs)
Logging an exceptions
There are multiple ways you could report a handled exception/error to Countly.
This call does not add a stacktrace automatically if it's needed, it should already be added to the exception variable, a potential use case would be to provide exception.toString()
Countly.logException(String exception, bool nonfatal, [Map<String, Object> segmentation])
Minimum Countly SDK Version
The following calls are only supported starting from the SDK version 20.04.1.
The issue is recorded with a provided Exception object. If no stacktrace is set,StackTrace.current
will be used.
Countly.logExceptionEx(Exception exception, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
The exception/error is recorded through a string message. If no stack trace is provided, StackTrace.current
will be used.
Countly.logExceptionManual(String message, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
View tracking
You can manually add your own views in your application, and each view will be visible under Views menu item. Below you can see two examples of sending a view using Countly.recordview
function.
// record a view on your application
Countly.recordView("HomePage");
Countly.recordView("Dashboard");
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
While manually tracking views, you may want to add custom segmentation to them.
Map<String, Object> segments = {
"Cats": 123,
"Moons": 9.98,
"Moose": "Deer"
};
Countly.recordView("HomePage", segments);
Application Performance Monitoring
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
This SDK provides a few mechanisms for APM. To start using them you would first need to enable this feature and give the required consent if it was required.
Countly.enableApm(); // Enable APM features, which includes the recording of app start time.
While using APM calls, you have the ability to provide trace keys by which you can track those parameters in your dashboard.
Custom trace
Currently, you can use custom traces to record the duration of application processes. At the end of them, you can also provide any additionally gathered data.
The trace key uniquely identifies the thing you are tracking and the same name will be shown in the dashboard. The SDK does not support tracking multiple events with the same key.
To start a custom trace, use:
Countly.startTrace(traceKey);
To end a custom trace, use:
String traceKey = "Trace Key";
Map<String, int> customMetric = {
"ABC": 1233,
"C44C": 1337
};
Countly.endTrace(traceKey, customMetric);
The provided Map of integer values that will be added to that trace in the dashboard.
Network trace
You can use the APM to track your requests. You would record the required info for your selected approach of making network requests and then call this after your network request is done:
Countly.recordNetworkTrace(networkTraceKey, responseCode, requestPayloadSize, responsePayloadSize, startTime, endTime);
`networkTraceKey` is a unique identifier of the API endpoint you are targeting or just the url you are targeting, all params should be stripped. You would also provide the received response code, sent payload size in bytes, received payload size in bytes, request start time timestamp in milliseconds, and request end finish timestamp in milliseconds.
Using Proguard
Proguard obfuscates the OpenUDID & Countly Messaging classes. If you use OpenUDID or Countly Messaging in your application, find app/proguard-rules.pro file which sits inside /android/app/ folder and adds the following lines:
-keep class org.openudid.** { *; }
-keep class ly.count.android.sdk.** { *; }
If Proguard is not already configured then first, enable shrinking and obfuscation in the build file. Find build.gradle file which sits inside /android/app/ folder and adds lines in bold
android {
buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true
// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
...
}
Next create a configuration that will preserve the entire Flutter wrapper code. Create /android/app/proguard-rules.pro file and insert inside:
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
More info related to code shrinking can be found here for flutter and android.
Attribution analytics & install campaigns
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
Countly Attribution Analytics allows you to measure your marketing campaign performance by attributing installs from specific campaigns. This feature is available for the Enterprise Edition.
Call this before init.
// Enable to measure your marketing campaign performance by attributing installs from specific campaigns.
Countly.enableAttribution();
Getting user feedback
There are two ways of getting feedback from your users: Star rating dialog and Feedback widget.
Star rating dialog allows users to give feedback as a rating from 1 to 5. The feedback widget allows to get the same 1 to 5 rating and also a text comment.
Feedback widget
Feedback widget shows a server configured widget to your user devices.
It's possible to configure any of the shown text fields and replace them with a custom string of your choice.
In addition to a 1 to 5 rating, it is possible for users to leave a text comment and also leave an email in case the user would want some contact from the app developer.
Trying to show the rating widget is a single call, but underneath is a two-step process. Before it is shown, the SDK tries to contact the server to get more information about the dialog. Therefore a network connection to it is needed.
You can try to show the widget after you have initialized the SDK. To do that, you first have to get the widget ID from your server:
Using that you can call the function to show the widget popup:
Countly.askForFeedback("5da0877c31ec7124c8bf398d", "Close");
Star rating dialog
Star rating integration provides a dialog for getting user's feedback about the application. It contains a title, simple message explaining what it is for, a 1-to-5 star meter for getting users rating and a dismiss button in case the user does not want to give a rating.
This star-rating has nothing to do with Google Play Store ratings and reviews. It is just for getting brief feedback from users, to be displayed on the Countly dashboard. If the user dismisses star rating dialog without giving a rating, the event will not be recorded.
Star-rating dialog's title, message and dismiss button text can be customized either through the init function or the SetStarRatingDialogTexts function. If you don't want to override one of those values, set it to "null".
Countly.askForStarRating();
Star rating dialog can be displayed in 2 ways:
- Manually, by developer
- Automatically, depending on session count
In order to display the Star rating dialog manually, you must call the ShowStarRating function. Optionally, you can provide callback functions. There is no limit on how many times star-rating dialog can be displayed manually.
Remote config
Remote config allows you to modify how your app functions or looks by requesting key-value pairs from your Countly server. The returned values can be modified based on the user profile. For more details please see Remote Config documentation.
Automatic remote config download
There are two ways of acquiring remote config data, by automatic download or manual request. By default, automatic remote config is disabled and therefore without developer intervention no remote config values will be requested.
Automatic value download happens when the SDK is initiated or when the device ID is changed. To enable it, you have to call setRemoteConfigAutomaticDownload before init. As an optional value you can provide a callback to be informed when the request is finished.
Countly.setRemoteConfigAutomaticDownload((result){
print(result);
});
If the callback returns a non-null value, then you can expect that the request failed and no values were updated.
When doing an automatic update, all locally stored values are replaced with the ones received (all locally stored ones are deleted and in their place are put new ones). It is possible that a previously valid key returns no value after an update.
Manual remote config download
There are three ways for manually requesting remote config update:
- Manually updating everything
- Manually updating specific keys
- Manually updating everything except specific keys
Each of these requests also has a callback. If that returns a non-null value, the request encountered some error and failed.
Functionally the manual update for everything remoteConfigUpdate
is the same as the automatic update - replaces all stored values with the ones from the server (all locally stored ones are deleted and in their place are put new ones). The advantage is that you can make the request whenever it is desirable for you. It has a callback to let you know when it has finished.
Countly.remoteConfigUpdate((result){
print(result);
});
You might want to update only specific key values. For that you need to call updateRemoteConfigForKeysOnly
with a list of keys you want to be updated. That list is an array with string values of those keys. It has a callback to let you know when the request has finished.
Countly.updateRemoteConfigForKeysOnly(["name"],(result){
print(result);
});
You might want to update all values except a few defined keys, for that call updateRemoteConfigExceptKeys
. The key list is an array with string values of the keys. It has a callback to let you know when the request has finished.
Countly.updateRemoteConfigExceptKeys(["url"],(result){
print(result);
});
When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a simple manual request and will update all values. This means that it will also erase all keys not returned by the server.
Getting remote config values
To request a stored value, call getRemoteConfigValueForKey with the specified key. If it returns null then no value was found. The SDK has no knowledge of the returned value type and therefore returns an Object. The developer needs to cast it to the appropriate type. The returned values can also be a JSONArray, JSONObject or just a simple value like int.
Countly.getRemoteConfigValueForKey("name", (result){
print(result);
});
Clearing stored remote config values
At some point you might want to erase all values downloaded from the server. To achieve that you need to call one function.
Countly.remoteConfigClearValues((result){
print(result);
});
User consent management
For compatibility with data protection regulations, such as GDPR, the Countly iOS SDK allows developers to enable/disable any feature at any time depending on user consent. More information about GDPR can be found here.
By default the requirement for consent is disabled. To enable it, you have to call setRequiresConsent
with true, before initializing Countly.
Countly.setRequiresConsent(true);
By default no consent is given. That means that if no consent is enabled, Countly will not work and no network requests, related to features, will be sent. When the consent status of a feature is changed, that change will be sent to the Countly server.
The Countly SDK does not persistently store the status of given consents except push notifications. You are expected to handle receiving consent from end-users using proper UIs depending on your app's context. You are also expected to store them either locally or remotely. Following this step, you will need to call thegiveConsent
methods on each app launch, right afterCountly.init
depending on the permissions you managed to get from the end-users.
The ideal location for giving consent is after Countly.init
and before Countly.start()
. Consent for features can be given and revoked at any time, but if it is given afterCountly.start()
, some features might work partially.
If consent is removed, but the appropriate function can't be called before the app closes, it should be done at next app start so that any relevant server-side features could be disabled (like reverse geo ip for location)
Currently, available features with consent control are as follows:
- sessions - tracking when, how often and how long users use your app
- events - allow sending custom events to the server
- views - allow tracking which views user visits
- location - allow sending location information
- crashes - allow tracking crashes, exceptions and errors
- attribution - allow tracking from which campaign did user come
- users - allow collecting/providing user information, including custom properties
- push - allow push notifications
- star-rating - allow sending their rating and feedback
- apm - allow application performance monitoring
Giving consents
To give consent for features, you can use the giveConsent
method by passing the feature names as an Array:
Countly.giveConsent(["events", "views", "star-rating", "crashes"]);
Removing consents
If the end-user changes his/her mind about consents at a later time, you will need to reflect this in the Countly SDK using the removeConsent
method:
Countly.removeConsent(["events", "views", "star-rating", "crashes"]);
Giving all consents
If you would like to give consent for all the features, you can use the giveAllConsent
method:
Countly.giveAllConsent();
Removing all consents
If you would like to remove consent for all the features, you can use the removeAllConsent
method:
Countly.removeAllConsent();
Troubleshooting
Warning about Java
Note: As per the new release, you will need Java 1.8 to bundle the application. You will need to update the CLASSPATH environment variable of Java and Javac to Java version 1.8
Push notifications
Android setup
Step 1: Hope you have created your flutter app, and installed countly_flutter package.
Step 2: Make sure you have google-services.json
from https://firebase.google.com/
Step 3: Make sure the app package name and the google-services.json
package_name
matches.
Step 4: Place the google-services.json
file inside android/app
Step 5: Add the following line in file android/app/src/main/AndroidManifest.xml
inside application
tag.
<service android:name="ly.count.dart.countly_flutter.CountlyMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Step 7: Use the latest version from this link https://firebase.google.com/support/release-notes/android#latest_sdk_versions and this link https://developers.google.com/android/guides/google-services-plugin
Step 6: Add the following line in file android/build.gradle
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.2'
}
}
Step 7: Add the following line in file android/app/build.gradle
dependencies {
implementation 'ly.count.android:sdk:20.04'
implementation 'com.google.firebase:firebase-messaging:20.0.0'
}
// Add this at the bottom of the file
apply plugin: 'com.google.gms.google-services'
iOS setup
For iOS push notification please follow the instruction from this URL https://resources.count.ly/docs/countly-sdk-for-ios-and-os-x#section-push-notifications
For Flutter you can find CountlyNotificationService.m
file under Pods/Pods/Countly/CountlyNotificationService.m
You can drag and drop the file from Pod to Compile Sources.
General setup
First, when setting up push for the Flutter SDK, you would first select the push token mode. This would allow you to choose either test or production modes, push token mode should be set before init.
// Set messaging mode for push notifications
Countly.pushTokenType(Countly.messagingMode["TEST"]);
Minimum Countly SDK Version
This feature is only supported by the minimum SDK version 20.04.1.
You may want to listen to a callback of when a push notification is received. You would register to that like this:
// Set callback to receive push notifications
Countly.onNotification((String notification){
print("The notification");
print(notification);
});
When you are finally ready to initialise Countly push, you would call this:
// This method will ask for permission, enables push notification and send push token to countly server.;
Countly.askForNotificationPermission();