A Flutter plugin that wraps the native Clickio Consent SDK for Android and iOS, providing a unified API for managing user consent in compliance with GDPR, TCF, GPP, and other regulations.
- Requirements
- Installation
- Quick Start
- Setup and Usage
- Methods
- Integration with Third-Party Libraries for Google Consent Mode
- Integration with Third-Party Libraries When Google Consent Mode Is Disabled
- Delaying Google Mobile Ads display until ATT and User Consent
- Running the Plugin Example App
Before integrating the ClickioConsentSdk (hereinafter reffered to as the Clickio SDK), ensure that your Flutter application meets the following requirements:
- Minimum SDK Version:
21
(Android 5.0) - Target/Compile SDK Version: The minimum required for Google Play compliance.
- Permissions: The SDK requires internet access. Add the following permissions to your
AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- Minimum iOS Version:
15.0+
- Swift Version:
5.0+
- Permissions: The SDK requires App Tracking Transparency (ATT) permission. Add the following permissions to your
Info.plist
:
<key>NSUserTrackingUsageDescription</key>
<string>Add your data usage description</string>
Run this command:
With Flutter:
$ flutter pub add clickio_consent_sdk
This will add a line like this to your pubspec.yaml
(and run an implicit flutter pub get
):
dependencies:
clickio_consent_sdk: ^1.0.0
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:clickio_consent_sdk/clickio_consent_sdk.dart';
Here’s the minimal implementation to get started with the clickio_consent_sdk
in a Flutter project.
import 'package:clickio_consent_sdk/clickio_consent_sdk.dart';
Replace your_clickio_site_id
with your actual Site ID.
final clickioConsentSdk = ClickioConsentSdk();
final config = Config(siteId: 'your_clickio_site_id');
Future<void> initializeSdk() async {
await clickioConsentSdk.setLogsMode(mode: LogsMode.verbose);
await clickioConsentSdk.initialize(config: config);
}
After initialization, open the consent window like this:
await clickioConsentSdk.openDialog(
mode: DialogMode.resurfaceMode,
attNeeded: true,
);
✅ After successful initialization, the SDK will open the consent dialog if it is required for the current user.
Clickio SDK
supports two distinct scenarios for handling ATT permissions If your app collects and shares user data with third parties for tracking across apps or websites, you must:
- Add the
NSUserTrackingUsageDescription
key in yourInfo.plist
. - Choose an ATT permission handling strategy using the
openDialog
method.
If you're managing ATT permissions manually and have already added NSUserTrackingUsageDescription
, you can skip ATT integration here and just use the consent flow.
- make sure that user has given permission in the ATT dialog and only then perfrom
openDialog
method call! Showing CMP regardles given ATT Permission is not recommended by Apple. Moreover,openDialog
API call can be blocked by Apple until user makes their choice.
👉 See User Privacy and Data Use and App Privacy Details for more details.
All methods should be accessed via a singleton instance of the SDK:
final clickioConsentSdk = ClickioConsentSdk();
Use the initialize
method to initialize the SDK:
await clickioConsentSdk.initialize(
config: Config(
siteId: 'your_clickio_site_id',
language: 'en', // language is optional
),
);
Config
parameters:
siteId
– Your Clickio Site ID (required)language
– Optional, 2-letter ISO 639-1 language code (e.g., 'en', 'de')
Make sure to use SDK methods after the SDK is fully ready. You can do this via your app lifecycle or after initialize
. (No explicit onReady
in Flutter – manage readiness with your app state.)
To open the consent dialog:
await clickioConsentSdk.openDialog(
mode: DialogMode.resurface, // or DialogMode.defaultMode
attNeeded: true,
);
mode
- defines when the dialog should be shown. Possible values::DialogMode.defaultMode
– Opens the dialog if GDPR applies and user hasn't given consent.DialogMode.resurface
– Always forces dialog to open, regardless of the user’s jurisdiction, allowing users to modify settings for GDPR compliance or to opt out under US regulations.
attNeeded
: Determines if ATT is required.
💡 If your app has it's own ATT Permission manager you just pass
false
inattNeeded
parameter and call your own ATT method. Keep in mind that in this case consent screen will be shown regardless given ATT Permission.
When your app's Android MainActivity is configured with launchMode="singleTask"
(set in AndroidManifest.xml
), returning to the app (e.g., via the launcher icon) can cause the Consent Dialog to close unexpectedly.
To ensure the Consent Dialog is displayed correctly when the launch mode set to "singleTask"
, handle opening consent dialog in Flutter’s didChangeAppLifecycleState callback.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_reopenConsentDialog();
}
}
Future<void> _reopenConsentDialog() async {
final consentState = await ClickioConsentSdk.checkConsentState();
if (consentState == ConsentState.gdprNoDecision) {
// Only show if user hasn't made a decision yet
await ClickioConsentSdk.openDialog(attNeeded: true);
} else {
debugPrint('Consent already decided, not reopening dialog.');
}
}
1. Show ATT Permission first, then show Consent Dialog only if user has granted ATT Permission:
(Apple recommended)
await clickioConsentSdk.openDialog(
mode: DialogMode.defaultMode,
attNeeded: true,
);
2. Show only Consent Dialog bypassing ATT Permission demonstration:
await clickioConsentSdk.openDialog(
mode: DialogMode.defaultMode,
attNeeded: false,
);
- we suggest you to use this approach only if you handle ATT Permission on your own.
- make sure that user has given permission in the ATT dialog and only then perfrom
openDialog
method call! Otherwise it will lead to incorrect work of the SDK: showing CMP regardles given ATT Permission is not recommended by Apple. Moreover,openDialog
API calls to SDK's domains will be blocked by Apple until user provides their permission in ATT dialog.
To enable or disable logging:
await clickioConsentSdk.setLogsMode(
mode: LogsMode.verbose,
);
mode
parameter defines whether logging is enabled or not:LogsMode.disabled
– Disables logging, default valueLogsMode.verbose
– Enables logging
Future<String> consentScope = clickioConsentSdk.getConsentScope();
Returns the applicable consent scope.
Value | Description |
---|---|
"gdpr" |
GDPR applies |
"us" |
US regulations apply |
"out of scope" |
Neither GDPR nor US laws apply |
Future<ConsentState> consentState = clickioConsentSdk.getConsentState();
Determines the current consent state based on the applicable scope and force flag.
Android | iOS | Description |
---|---|---|
NOT_APPLICABLE |
notApplicable |
Consent is not required for the current region |
GDPR_NO_DECISION |
gdprNoDecision |
GDPR applies, no user decision made |
GDPR_DECISION_OBTAINED |
gdprDecisionObtained |
GDPR applies and decision obtained |
US |
us |
US privacy laws apply |
⚠️ Note: Enum casing differs by platform: Android uses UPPER_SNAKE_CASE, iOS uses camelCase.
Future<bool> consentForPurpose = clickioConsentSdk.getConsentForPurpose(
purposeId: purposeId,
);
Checks whether consent was granted for a specific TCF purpose.
purposeId
(int
): The numeric ID of the TCF purpose.
Future<bool> consentForVendor = clickioConsentSdk.getConsentForVendor(
vendorId: vendorId,
);
Checks whether consent was granted for a specific TCF vendor.
vendorId
(int
): The numeric ID of the vendor.
These methods allow you to query consent strings and granted vendor/purpose IDs, mirroring behavior from iOS/Android.
Returns the IAB TCF v2.2 string if it exists.
Returns the Google additional consent string if it exists.
Returns the Global Privacy Platform (GPP) string if it exists.
Returns the IDs of TCF vendors that have given consent.
Returns the IDs of TCF vendors that have given consent for legitimate interests.
Returns the IDs of TCF purposes that have given consent.
Returns the IDs of TCF purposes that have given consent as Legitimate Interest.
Returns the IDs of Google vendors that have given consent.
Returns the IDs of non-TCF vendors that have given consent.
Returns the IDs of non-TCF vendors that have given consent for legitimate interests.
Returns the IDs of non-TCF purposes (simplified purposes) that have given consent.
Returns Google Consent Mode v2 flags wrapped into GoogleConsentStatus
struct if Google Consent Mode enabled, otherwise will return false
.
class GoogleConsentStatus {
final bool analyticsStorageGranted;
final bool adStorageGranted;
final bool adUserDataGranted;
final bool adPersonalizationGranted;
GoogleConsentStatus({
required this.analyticsStorageGranted,
required this.adStorageGranted,
required this.adUserDataGranted,
required this.adPersonalizationGranted,
});
}
analyticsStorageGranted
— Consent for analytics storageadStorageGranted
— Consent for ad storageadUserDataGranted
— Consent for processing user data for adsadPersonalizationGranted
— Consent for ad personalization
ClickioConsentSDK provides automatic support for transmitting Google Consent Mode v2 flags to popular third-party platforms when enabled:
If the Firebase Analytics SDK is present in your project:
- ClickioConsentSDK will automatically send Google Consent Mode flags to Firebase if integration is enabled.
- Flags are transmitted:
- Immediately after the consent dialog update (
onConsentUpdated
) - Or on SDK initialization if consent has already been given
- Immediately after the consent dialog update (
- If logging is enabled:
- A success message will confirm the transmission.
- In case of errors, a log error message will appear.
- Make sure you're using a recent version of Firebase Analytics in your project for best results.
If your project includes any of these SDKs (Adjust, Airbridge, AppsFlyer), ClickioConsentSDK
will automatically send Google Consent flags to them if Clickio Google Consent Mode integration is enabled.
- Interactions with
ClickioConsentSDK
should be performed after initializing the third-party SDKs sinceClickioConsentSDK
only transmits consent flags. - Ensure that you have completed the required tracking setup for Adjust, Airbridge, or AppsFlyer before integrating
ClickioConsentSDK
. This includes proper initialization and configuration of the SDK according to the vendor’s documentation. - If you're using AppsFlyer and need to support GDPR compliance via TCF, make sure to enable TCF data collection before SDK initialization:
enableTCFDataCollection(true)
. This allows AppsFlyer to automatically gather consent values (liketcString
) from the CMP.
After successfully transmitting the flags, a log message will be displayed (if logging is enabled) to confirm the successful transmission. In case of an error, an error message will appear in the logs.
💡 Note: Keep your Adjust, Airbridge, or AppsFlyer SDK updated to ensure compatibility.
If Google Consent Mode integration is disabled, you can manually determine user consent and set flags accordingly for other SDKs like Firebase Analytics.
final purpose1 = await clickioConsentSdk.getConsentForPurpose(purposeId: 1);
final purpose3 = await clickioConsentSdk.getConsentForPurpose(purposeId: 3);
final purpose4 = await clickioConsentSdk.getConsentForPurpose(purposeId: 4);
final purpose7 = await clickioConsentSdk.getConsentForPurpose(purposeId: 7);
final purpose8 = await clickioConsentSdk.getConsentForPurpose(purposeId: 8);
final purpose9 = await clickioConsentSdk.getConsentForPurpose(purposeId: 9);
final adStorage = purpose1;
final adUserData = purpose1 && purpose7;
final adPersonalization = purpose3 && purpose4;
final analyticsStorage = purpose8 && purpose9;
// Set Firebase Analytics consent flags
await FirebaseAnalytics.instance.setConsent(
adStorageConsentGranted: adStorage,
adUserDataConsentGranted: adUserData,
adPersonalizationSignalsConsentGranted: adPersonalization,
analyticsStorageConsentGranted: analyticsStorage
);
📚 More about Consent Mode flags mapping with TCF and non-TCF purposes
Sometimes you need to ensure that both Apple's App Tracking Transparency prompt and user consent decision have been recorded before initializing and loading Google Mobile Ads. To implement this flow:
- Wait for ATT authorization and CMP readiness
- First present the ATT prompt.
- Then open Clickio SDK's consent dialog via
ClickioConsentSDK.openDialog()
.
- Use Clickio SDK's callbacks
- In the
onReady
callback, you know that SDK is ready & the CMP dialog can be shown. - In the
onConsentUpdated
callback, you know the user's consent decision has been recorded.
- Initialize and load ads only after consent
- Ensure that
checkConsentState() != gdprNoDecision
has been confirmed. - Then call
MobileAds.instance.initialize(...)
and load your banner. This ensures that Google Mobile Ads is only started - and the banner only fetched — once you've obtained both ATT permission and explicit user decision from the CMP.
– E.g. if ads already started on initial accept, don’t restart after a later “resurface” consent change.
// Keep track of whether Google Mobile Ads has already been started
bool _adsStarted = false;
void setupConsentAndAds() {
// 1) Listen for SDK readiness (CMP can show)
ClickioConsentSdk.onReady.listen((_) {
// 2) Show CMP dialog
ClickioConsentSDK.openDialog(
mode: DialogMode.defaultMode,
attNeeded: true,
);
// 3) Immediately check prior consent
_tryStartAdsIfAllowed();
});
// 4) When consent changes, check again
ClickioConsentSdk.onConsentUpdate.listen((_) {
_tryStartAdsIfAllowed();
});
}
Future<void> _tryStartAdsIfAllowed() async {
final consentState = await ClickioConsentSDK.checkConsentState();
if (consentState == ConsentState.gdprNoDecision) return;
if (_adsStarted) {
debugPrint('Ads already started');
return;
}
debugPrint('Consent allows ads – starting Google Mobile Ads');
await MobileAds.instance.initialize();
_adsStarted = true;
_loadBannerAd(); // Optional: Load your ad here
}
void _loadBannerAd() {
// Load a simple banner ad
BannerAd(
adUnitId: '<YOUR_AD_UNIT_ID>',
size: AdSize.banner,
request: AdRequest(),
listener: BannerAdListener(),
).load();
}
To get started with the plugin example application, follow the simple steps below. This will help you set up and run the app on your local machine.
- Clone the repository:
git clone <repository-url>
cd <repository-folder>/example
- Get dependencies:
flutter pub get
- Run the example app:
flutter run