Leading Consent Management Platform

Compliant with GDPR, CCPA, COPPA, LGPD, PECR, PDPA, PIPEDA, and more.

如何将 UniConsent CMP SDK 集成到 Flutter 移动应用

UniConsent CMP 是用于在 Flutter 应用中处理 GDPR IAB TCF 2.4 同意管理的软件包。您可以在 "uniconsent_demo" 目录中找到集成了 UniConsent CMP 的演示应用。

前提条件

  • 支持移动应用的 UniConsent CMP 方案
  • UniConsent CMP SDK 软件包(请联系支持团队获取)

开始使用

要在 Flutter 项目中包含 UniConsent 软件包,请将其作为依赖项添加到 pubspec.yaml 文件中:

dependencies:
  uniconsent:
    path: ../uniconsent

然后运行以下命令同步依赖项:

flutter pub get

自定义同意界面

在集成 SDK 之前,请在 UniConsent 控制台 中自定义同意横幅的外观,以匹配您的品牌并优化同意率。

第 1 步:在控制台中设置品牌样式

前往 Projects → 选择您的项目 → Settings → Step 5: UI & Style Settings 进行配置:

  • Main Button Colour — 设置主操作按钮颜色以匹配您的品牌
  • Main Button Text Colour — 调整主按钮上的文字颜色以确保可读性
  • Background Colour — 设置横幅背景颜色以融入您的应用
  • Text Colour — 确保正文文字具有适当的对比度

第 2 步:使用自定义 CSS 进行高级样式设置(可选)

如需更精确的控制,请在 Step 5 的 CSS Content 字段中添加自定义 CSS。建议使用此功能让横幅呈现原生应用的观感,以获得最佳同意率:

/* Example: Style the accept button to match your brand */
.unic-btn-accept {
  background-color: #4CAF50;
  border-radius: 8px;
  font-weight: 600;
}

/* Example: Adjust banner font */
.unic-banner {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* Example: Make the reject button less prominent */
.unic-btn-reject {
  background-color: transparent;
  border: 1px solid #ccc;
  color: #666;
}

提示: 具有原生应用观感的精心设计的同意界面通常能获得更高的同意率。用户更可能对符合预期外观和风格的横幅做出积极响应。

使用方法

要在应用中使用 UniConsent CMP,请按照以下步骤操作:

使用客户经理提供的 App ID 初始化 CMP:

// Init CMP with appId
UniConsent.instance.setAppId("YOUR_APP_ID_CHANGE_THIS");
// Set language (Optional)
UniConsent.instance.setLanguage("EN");

显示 CMP 界面:

// Display CMP as full-screen page (default)
UniConsent.instance.launchCMP(context);

// Display CMP as a modal bottom sheet
UniConsent.instance.launchCMP(context, displayMode: CMPDisplayMode.modalSheet);

// Display CMP as a modal bottom sheet at 70% of the available height
UniConsent.instance.launchCMP(
  context,
  displayMode: CMPDisplayMode.modalSheet,
  heightRatio: 0.7,
);

// Display CMP as a center dialog
UniConsent.instance.launchCMP(context, displayMode: CMPDisplayMode.dialog);

// Display CMP as a center dialog at 85% width and 70% height
UniConsent.instance.launchCMP(
  context,
  displayMode: CMPDisplayMode.dialog,
  widthRatio: 0.85,
  heightRatio: 0.7,
);

自定义尺寸(可选)

自 SDK 26.8.0 版本起可用。

两个比例参数均为可选,所有显示模式不传它们也能正常工作。仅在需要覆盖默认尺寸时才传入比例(相对可用空间的比例,需大于 0 且不超过 1);无效值会抛出 ArgumentError:

参数适用于省略时的默认值
heightRatiomodalSheet、dialog面板:高度的 90%。对话框:70%
widthRatio仅 dialog宽度的 90%,最大 500 逻辑像素

不适用于所选显示模式的比例会被忽略(page 会忽略两者)。

事件处理

使用 subscribe() 监听 SDK 生命周期事件。这是了解界面何时显示和关闭的推荐方式。

可用事件(CMPEventType):

  • CMPEventType.uiDisplay — 同意界面已显示
  • CMPEventType.uiClose — 同意界面已关闭
UniConsent.instance.subscribe((event) {
  switch (event.type) {
    case CMPEventType.uiDisplay:
      print('CMP UI displayed');
      break;
    case CMPEventType.uiClose:
      print('CMP UI closed');
      break;
    default:
      break;
  }
});

使用可选回调监听同意变更:

// Display CMP UI with consent change callback
UniConsent.instance.launchCMP(context, onConsentChanged: (info) {
  // Called when the user saves consent choices
  print('Consent updated: ${info?.tcString}');
});

如需完全控制,使用 buildCMPWidget() 将 CMP 嵌入您自己的布局中:

// Embed in your own widget tree
UniConsent.instance.buildCMPWidget(
  onConsentChanged: (info) { /* ... */ },
  onClose: () { /* dismiss your custom UI */ },
);

在应用启动时自动检查是否需要同意:

// Check if consent should be requested (e.g. first visit, or vendor list updated)
final shouldRequest = await UniConsent.instance.shouldRequestConsent();
if (shouldRequest) {
  UniConsent.instance.launchCMP(context);
}

获取 tcString(如需要):

// Get tcString
final info = await UniConsent.instance.getConsentInfo();
print(info?.tcString);

读取同意状态:

// Read consent status
final info = await UniConsent.instance.getConsentInfo();
if (info is ConsentInfo &&
    info.purposeConsents.contains(DataUsagePurpose.useLimitedDataToSelectAds)) {
  // do something
}

重置同意状态(如需要):

// Clear all consent data
await UniConsent.instance.clearAll();

自定义首层界面(全部同意 / 全部拒绝)

自 SDK 26.7.0 版本起可用。如果您构建自己的首层同意界面而不显示 CMP webview,可以直接保存同意状态:

// Grant consent for all purposes, special features and vendors
final saved = await UniConsent.instance.agreeAll();

// Reject all purposes, special features and vendors
final saved = await UniConsent.instance.rejectAll();

这两个 API 保存与 CMP UI 流程相同的同意值(IAB TCF 键、附加同意和同意模式信号),并向订阅者发送相同的关闭事件。

对于美国和加拿大用户,这两个 API 还会按照与 CMP 界面相同的规则,保存适用于该用户的 CCPA、US State Privacy 和 IAB TCF 加拿大选择,并写入 IAB GPP 和 US Privacy 键。请在 UniConsent 控制台的项目设置中开启 API-Only 模式,这些地区便不会显示 CMP 横幅。

在适用 Cookie 类别横幅的地区,它们还会保存 Cookie 类别选择(全部类别,或仅“绝对必要”),以及项目的自定义目的和自定义供应商。

要确定显示哪个首层界面,请调用 getPolicy()(自 SDK 26.10.0 版本起)。它按照与 CMP 相同的规则返回适用于该用户的政策:gdpr(IAB TCF)、tcfca(IAB TCF 加拿大)、uss(US State Privacy)、ccpa、categories(见下文 Cookie 类别)或 none。

final cmp = UniConsent.instance;
if (await cmp.shouldRequestConsent()) {
  // 用户需要做出选择:显示该政策对应的首层界面
  final policy = await cmp.getPolicy(); // gdpr、tcfca 或 categories
}

注意: 这些 API 通过网络获取供应商列表和项目配置,如果无法保存同意状态(例如离线时)则返回 false。

重要: 您的自定义首层界面仍须满足 IAB TCF 要求(披露目的和供应商,并以同等显著程度提供接受和拒绝选项)。用户仍应能够打开完整的 CMP 界面(launchCMP())进行精细选择。

将同意状态同步到 WebView

如果您的应用打开了加载带有 UniConsent CMP 标签的网页的 WebView,您可以传递原生同意状态,这样 CMP 标签会识别已有的同意记录,不会再次显示横幅。

import 'package:webview_flutter/webview_flutter.dart';
import 'package:uniconsent/uniconsent.dart';

final controller = WebViewController();
controller
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..setNavigationDelegate(NavigationDelegate(
    onPageStarted: (url) async {
      // Inject consent at page start, before CMP tag runs
      await UniConsent.instance.syncConsent(controller);
    },
  ))
  ..loadRequest(Uri.parse('https://example.com'));

导出的类型

导入 package:uniconsent/uniconsent.dart 后可使用以下类型:

  • UniConsent — SDK 主单例
  • UniConsentInfo — 基础同意数据(sdkId、tcString、gdprApplies、vendorListVersion 等)
  • ConsentInfo — 继承自 UniConsentInfo,包含目的级别的同意列表
  • DataUsagePurpose — IAB TCF 2.4 目的 ID 枚举
  • KVDBKeys — IAB TCF 存储键常量(例如 KVDBKeys.tcString、KVDBKeys.purposeConsents)
  • CMPDisplayMode — 显示样式枚举(page、modalSheet、dialog)
  • CMPEventType — 生命周期事件枚举(SDK 会触发 uiDisplay 和 uiClose)
  • CMPEvent — 带有 type 属性的事件对象
  • CMPEventCallback — subscribe() 回调的类型定义
  • LogLevel — debug / prod
  • ConsentCallback — onConsentChanged 回调的类型定义
  • UniConsentCMPContent — 用于自定义布局的核心 CMP 组件

打开指定界面及其他选项

可以在指定界面打开 CMP,例如从应用中的“隐私设置”链接打开。请求的界面总会打开。对于美国用户,设置界面会打开适用于他们的 CCPA 或 US State Privacy 设置。加拿大用户看到的是 IAB TCF 界面的 IAB TCF Canada 版本。

界面显示内容
CMPStage.gdprFirstScreen首层界面(横幅)
CMPStage.gdprSecondScreen设置(目的)
CMPStage.gdprVendorsScreen供应商
CMPStage.ccpaFirstScreenCCPA 首层界面
CMPStage.ccpaSecondScreenCCPA 设置
CMPStage.usStateFirstScreenUS State Privacy 首层界面
CMPStage.usStateSecondScreenUS State Privacy 设置
CMPStage.categoriesFirstScreenCookie 类别首层界面(横幅)
CMPStage.categoriesSecondScreenCookie 类别设置
final cmp = UniConsent.instance;

// 打开设置界面
cmp.launchCMP(context, stage: CMPStage.gdprSecondScreen);

// 仅在需要同意时打开 CMP(欧盟和 IAB TCF 加拿大),返回是否已打开
final opened = await cmp.launchCMPIfNeeded(context);

// 调试日志,正式版本请使用 LogLevel.prod
cmp.setLogLevel(LogLevel.debug);

// 仅用于测试:按其他地区的用户显示 CMP,"EU"、"CA"(加拿大)、"CAQC"(魁北克)、"US" 加上州代码(如 "USCA" 或 "USTX")、"BR"、"CN"、"ZA"、"VN" 或其他任何国家代码,或 "N"(以上地区以外)。也适用于 getPolicy() 和 shouldRequestConsent();正式版本中请删除
cmp.setTestRegion("CA");

// 用户的国家代码(未知时为 "AA"),以及美国和加拿大用户的州或省代码
final geo = await cmp.getGEO();
final state = await cmp.getGEOState();

IAB GPP 与 US Privacy

CMP 会按照 IAB 规范,将 IAB GPP 键和 US Privacy 键存储到应用的原生存储(Android 为 SharedPreferences,iOS 为 UserDefaults)中。应用内的广告 SDK 会从那里读取,无需修改代码。在 UniConsent 控制台的项目设置中启用 GPP、US State Privacy 或 IAB TCF 加拿大后会写入 GPP 键;只要启用 CCPA,就会写入 US Privacy 键。

键值
IABGPP_HDR_VersionGPP 版本(1)
IABGPP_HDR_SectionsGPP 字符串中的部分 ID,以 _ 分隔
IABGPP_HDR_GppString完整的 GPP 字符串
IABGPP_GppSID适用于该用户的部分 ID,以 _ 分隔;均不适用时为 -1
IABGPP_2_StringIAB TCF EU 字符串(适用 GDPR 时)
IABGPP_5_StringIAB TCF 加拿大字符串(适用加拿大 TCF 时)
IABGPP_6_StringUS Privacy 字符串(适用 CCPA 时)
IABGPP_7_StringUS National(usnat)字符串(适用 US State Privacy 或 CCPA 时)
IABGPP_8_String 至 IABGPP_27_String用户所在州的部分(8 加利福尼亚至 27 罗得岛),适用 US State Privacy 时写入,或在 CCPA 下写入加利福尼亚,遵循 GPP Sections 设置
IABUSPrivacy_String只要启用 CCPA 就会写入:在 CCPA 地区为用户的选择,否则为 1---

每次加载 CMP 以及每次做出同意选择后,这些键都会更新。关闭 GPP 后,IABGPP_ 键会被删除。

美国和加拿大

应用支持与网站相同的美国和加拿大选项:CCPA、US State Privacy(包括州列表、GPP Sections 和 MSPA 设置)以及 IAB TCF 加拿大。对于适用 IAB TCF 加拿大的加拿大用户,在其做出选择之前,shouldRequestConsent() 会返回 true。美国隐私采用退出(opt-out)模式,因此不会主动询问美国用户:当您的应用打开 CMP 时(例如通过隐私设置链接),他们会看到美国横幅或设置。在此之前,启用 CCPA 或 US State Privacy 时,SDK 会为美国用户写入默认的 US Privacy 和 GPP 键,不显示任何内容:未退出;若设备关闭了广告跟踪,则为已退出。新的 Mobile App 项目默认关闭 US State Privacy,请在控制台中开启。

设备广告跟踪退出

当用户在设备上关闭广告跟踪时,CMP 会将其视为美国用户对出售、共享和定向广告的退出,与网站上的 Global Privacy Control 相同。在 iOS 上指 App Tracking Transparency 中不允许跟踪;在 Android 上指已删除广告 ID 或限制广告跟踪(应用包含 Google Play 服务时通过其读取)。用户之后在横幅中保存的选择优先。

读取 GPP 和 US Privacy 字符串

SDK 也会返回已存储的字符串:

final gppString = await UniConsent.instance.getGPPString();
final gppSid = await UniConsent.instance.getGPPSID();
final usPrivacy = await UniConsent.instance.getUSPrivacyString();

自 SDK 26.10.0 版本起可用。当某个地区使用 Cookie 类别而不是 IAB TCF 或美国政策时,CMP 会显示包含四个类别的横幅:绝对必要、性能、功能性、定向或广告。适用于:

  • 巴西的 LGPD、中国的 PIPL、南非的 POPIA 和越南的 PDPD(在 UniConsent 控制台的项目设置中开启时)
  • 您为其他国家设置的规则:针对某个国家的规则,以及针对其余所有国家的规则
  • Consent Categories 项目,在其设置的地区

选择加入(opt-in)类别初始只开启“绝对必要”,在用户做出选择之前,以及您更改配置版本之后,shouldRequestConsent() 会返回 true。选择退出(opt-out)类别初始开启所有类别(在美国,若设备关闭了广告跟踪,则关闭“定向或广告”),应用不会主动询问:用户可通过您的隐私设置链接更改。选择退出适用于其他国家的 opt-out 规则,以及应用中位于美国的 Consent Categories 项目。

使用 CMPStage.categoriesFirstScreen(横幅)和 CMPStage.categoriesSecondScreen(设置)打开类别界面。setTestRegion() 也支持 BR、CN、ZA、VN 及其他国家代码,用于测试这些地区。

自定义目的和自定义供应商

自 SDK 26.10.0 版本起可用。您在 UniConsent 控制台项目设置中添加的自定义目的和自定义供应商会显示在 CMP 界面中:自定义目的显示在 IAB TCF 首层界面和设置以及 Cookie 类别设置中,自定义供应商显示在 IAB TCF 供应商列表中。在 CCPA 和 US State Privacy 设置中,只列出敏感的自定义目的。在 Cookie 类别设置中,自定义供应商跟随“定向或广告”类别。在不适用 IAB TCF 的地区(Cookie 类别和美国),自定义供应商以只读方式列在菜单的供应商列表中,并在 Cookie 类别设置的“定向或广告”下列出。

要据此启动或停止您自己的 SDK,请询问 SDK 应用现在是否可以使用某个自定义目的或自定义供应商。结果遵循用户适用的政策,规则与 CMP 用于标签的规则相同:

  • IAB TCF、IAB TCF 加拿大和选择加入(opt-in)的 Cookie 类别:仅在用户同意后允许
  • 美国和选择退出(opt-out)的 Cookie 类别:在用户退出之前允许;敏感的自定义目的需要用户同意,在美国若设备关闭了广告跟踪,则自定义供应商关闭
  • 无政策:允许
final cmp = UniConsent.instance;
if (await cmp.isCustomPurposeConsented(1)) {
  // 启动自定义目的 1 涵盖的 SDK
}
final vendorAllowed = await cmp.isCustomVendorConsented(2);
final all = await cmp.getCustomConsents(); // {'purposes': {'1': true}, 'vendors': {'2': false}}

在 CMP 关闭或同意变化后请再次调用。项目中不存在的 ID,以及 SDK 就绪前的所有 ID,都返回 false。

第三方 SDK 披露

CMP 可以列出您应用中的第三方 SDK(广告网络、分析、归因等),包括其公司、用途和隐私政策。用户在供应商列表中查看它们,在所有地区均为只读:没有开关,您的应用仍根据用户对目的或 Cookie 类别的选择来开启或关闭各个 SDK。

要设置列表,请在应用项目的 Step 4: Advanced Features 中开启 Enable Third-party SDKs,在 Third-party SDKs 标签页中选择您的 SDK 或添加自己的 SDK,然后保存并发布。您的应用中无需任何代码。

注意事项

  1. 用户应该能够在应用的设置部分访问"隐私设置"按钮或链接,以便打开 CMP 界面。
  2. 您可以使用 shouldRequestConsent() 函数来检查是否需要根据状态请求新的同意。在用户打开应用时,根据需要显示 CMP 界面。

更新日志

26.10.0

  • 为广告 SDK 写入 IAB GPP 和 US Privacy 键
  • 应用支持 US State Privacy 和 IAB TCF 加拿大,包括自定义首层界面
  • 新增 getGPPString()、getGPPSID() 和 getUSPrivacyString()
  • 使用 launchCMP(context, stage:) 打开指定界面;新增 launchCMPIfNeeded()、getGEO() 和 getGEOState()
  • 使用 CMPStage.ccpaFirstScreen 和 CMPStage.usStateFirstScreen 打开 CCPA 和 US State Privacy 首层界面;新增 setTestRegion(),可按其他地区(例如加拿大)的用户测试 CMP
  • 应用支持 Cookie 类别:LGPD(巴西)、PIPL(中国)、POPIA(南非)、PDPD(越南)、您为其他国家设置的规则以及 Consent Categories 项目,界面为 CMPStage.categoriesFirstScreen 和 CMPStage.categoriesSecondScreen
  • 新增 getPolicy();agreeAll() 和 rejectAll() 还会保存 Cookie 类别、自定义目的和自定义供应商
  • setTestRegion() 也支持 BR、CN、ZA、VN 及其他国家代码,并适用于 getPolicy() 和 shouldRequestConsent()
  • 新增 isCustomPurposeConsented()、isCustomVendorConsented() 和 getCustomConsents():应用按照用户适用的政策现在可以使用的自定义目的和自定义供应商

26.9.0

  • 支持 IAB TCF 2.4