遵守数据隐私法
通过通知Singular用户同意选择GDPR、CCPA、COPPA和其他消费者隐私法规,实施符合隐私法规的数据收集。
当用户同意或拒绝与第三方共享其信息时,使用 Singular 的隐私方法来传达他们的选择。这将确保遵守《加州消费者隐私法案》(CCPA)等法规,并使合作伙伴尊重用户的隐私偏好。
了解更多信息:有关Singular如何处理隐私同意的详细信息,请参阅用户隐私和限制数据共享。
限制数据共享
控制第三方数据共享
通知Singular用户是否同意使用limitDataSharing() 方法与第三方合作伙伴共享个人数据。
方法签名:
static void limitDataSharing(bool shouldLimitDataSharing)
参数:
- false:用户已选择并同意共享其数据
- true:用户已退出,不同意共享其数据
重要:虽然此方法是可选的,但它会影响属性数据的共享。有些合作伙伴只有在明确通知用户已选择加入时,才会共享完整的归因信息。
有关完整的方法文档,请参阅limitDataSharing 参考资料。
使用示例
根据用户隐私偏好实施数据共享控制。
import 'package:singular_flutter_sdk/singular.dart';
// User has opted in to share their data
void onUserOptedInToDataSharing() {
Singular.limitDataSharing(false);
print('Data sharing enabled');
}
// User has opted out and declined to share their data
void onUserOptedOutOfDataSharing() {
Singular.limitDataSharing(true);
print('Data sharing limited');
}
// Set based on user preference
void handlePrivacyConsent(bool userConsented) {
// Pass inverse: false = opted in, true = opted out
Singular.limitDataSharing(!userConsented);
print('Data sharing: ${userConsented ? 'Enabled' : 'Limited'}');
}
如何使用
Singular在用户隐私回邮中使用此设置,并将其传递给需要遵守法规的合作伙伴。
GDPR 合规方法
管理用户跟踪同意和控制 SDK 功能,以符合 GDPR(通用数据保护条例)和其他隐私法规。
跟踪同意管理
跟踪同意
通过向 Singular 服务器发送 GDPR 选入事件,记录用户对跟踪的明确同意。
方法签名:
static void trackingOptIn()
何时使用:
- GDPR 合规性:当用户明确同意在受 GDPR 监管的地区进行跟踪时调用
- 同意记录:在 Singular 系统中将用户标记为已提供 GDPR 同意
- 默认行为:如果没有此调用,SDK 会继续跟踪,但不会明确记录同意信息
有关完整的方法文档,请参阅trackingOptIn 参考资料。
实现示例
在用户通过应用程序的同意对话框接受跟踪同意后调用trackingOptIn() 。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:shared_preferences/shared_preferences.dart';
class GDPRManager extends StatefulWidget {
@override
_GDPRManagerState createState() => _GDPRManagerState();
}
class _GDPRManagerState extends State<GDPRManager> {
bool? hasConsent;
@override
void initState() {
super.initState();
checkStoredConsent();
}
Future<void> checkStoredConsent() async {
final prefs = await SharedPreferences.getInstance();
final consent = prefs.getString('gdpr_consent');
if (consent == null) {
// Show consent dialog
showGDPRConsentDialog();
} else {
setState(() {
hasConsent = consent == 'true';
});
}
}
void showGDPRConsentDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text('Privacy Consent'),
content: Text(
'We would like your permission to track app usage and improve your experience.'
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
setState(() {
hasConsent = false;
});
},
child: Text('Decline'),
),
ElevatedButton(
onPressed: () async {
Navigator.of(context).pop();
await onUserAcceptedTracking();
},
child: Text('Accept'),
),
],
),
);
}
Future<void> onUserAcceptedTracking() async {
// Record user consent
Singular.trackingOptIn();
print('User opted in to tracking');
// Save preference
final prefs = await SharedPreferences.getInstance();
await prefs.setString('gdpr_consent', 'true');
setState(() {
hasConsent = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: hasConsent != null
? Text('Privacy Status: ${hasConsent! ? 'Opted In' : 'Declined'}')
: CircularProgressIndicator(),
),
);
}
}
跟踪控制方法
停止所有跟踪
完全禁用当前用户在此设备上的所有 SDK 跟踪活动。
方法签名:
static void stopAllTracking()
严重警告:此方法会永久禁用 SDK,直到调用resumeAllTracking() 。禁用状态会在应用程序重启时持续存在,只能通过编程方式逆转。
行为:
- 立即生效:立即停止所有跟踪、事件报告和数据收集功能
- 持续状态:即使在应用程序关闭和重新打开后也会保持禁用状态
-
无法自动重置:必须明确调用
resumeAllTracking()才能重新启用
有关完整的方法文档,请参阅stopAllTracking 参考资料。
实施示例
当用户拒绝同意或通过隐私设置选择退出时停止跟踪。
import 'package:singular_flutter_sdk/singular.dart';
import 'package:shared_preferences/shared_preferences.dart';
// User declined all tracking
Future<void> onUserDeclinedTracking() async {
Singular.stopAllTracking();
print('All tracking stopped');
// Store preference
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('tracking_enabled', false);
}
// Handle user opt-out from settings menu
void handlePrivacySettingsChange(bool trackingEnabled) {
if (!trackingEnabled) {
Singular.stopAllTracking();
print('Privacy settings: Tracking disabled');
}
}
恢复全部跟踪
在使用stopAllTracking() 停止跟踪后重新启用跟踪。
方法签名:
static void resumeAllTracking()
使用案例:
- 同意变更:用户更改隐私偏好并选择恢复跟踪
- 隐私设置:用户通过应用程序设置菜单更新同意
- 地区合规性:当用户转移到非监管区域时重新启用跟踪功能
有关完整的方法文档,请参阅resumeAllTracking 参考资料。
实施示例
当用户重新选择或更新隐私偏好时恢复跟踪。
import 'package:singular_flutter_sdk/singular.dart';
import 'package:shared_preferences/shared_preferences.dart';
// User opted back in to tracking
Future<void> onUserResumedTracking() async {
Singular.resumeAllTracking();
print('Tracking resumed');
// Update stored preference
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('tracking_enabled', true);
}
// Handle consent update from settings
void handlePrivacySettingsChange(bool trackingEnabled) {
if (trackingEnabled) {
Singular.resumeAllTracking();
print('Privacy settings: Tracking enabled');
}
}
IsAllTrackingStopped
检查当前用户的跟踪是否已被禁用。
方法签名:
static Future<bool> isAllTrackingStopped()
返回:
-
Future<true>(未来<true>):当前已通过
stopAllTracking()停止跟踪 - Future<false>:跟踪处于激活状态:跟踪处于活动状态(从未停止或恢复
有关该方法的完整文档,请参阅isAllTrackingStopped 参考文献。
实现示例
检查跟踪状态,使 UI 状态与 SDK 跟踪状态同步。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
class PrivacySettingsUI extends StatefulWidget {
@override
_PrivacySettingsUIState createState() => _PrivacySettingsUIState();
}
class _PrivacySettingsUIState extends State<PrivacySettingsUI> {
bool trackingEnabled = false;
String statusText = 'Checking...';
@override
void initState() {
super.initState();
updatePrivacyUI();
}
// Check current tracking status
Future<bool> isTrackingEnabled() async {
final isStopped = await Singular.isAllTrackingStopped();
return !isStopped;
}
// Display privacy status in settings
Future<String> getPrivacyStatusText() async {
final isStopped = await Singular.isAllTrackingStopped();
return isStopped ? 'Tracking: Disabled' : 'Tracking: Enabled';
}
// Sync UI with tracking state
Future<void> updatePrivacyUI() async {
final isStopped = await Singular.isAllTrackingStopped();
final status = await getPrivacyStatusText();
setState(() {
trackingEnabled = !isStopped;
statusText = status;
});
print('Current tracking state: ${isStopped ? 'Stopped' : 'Active'}');
}
// Handle toggle change from UI
Future<void> onTrackingToggleChanged(bool enabled) async {
if (enabled) {
Singular.resumeAllTracking();
} else {
Singular.stopAllTracking();
}
await updatePrivacyUI();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(statusText),
Switch(
value: trackingEnabled,
onChanged: onTrackingToggleChanged,
),
],
);
}
}
儿童隐私保护
跟踪13岁以下儿童
通知 Singular 用户未满 13 周岁,以遵守 COPPA(儿童在线隐私保护法案)和其他儿童隐私法规。
方法签名:
static void trackingUnder13()
合规要求:
- COPPA 合规性:在美国收集 13 岁以下儿童数据的应用程序必须遵守
- 有年龄限制的内容:当用户在注册或年龄验证时表明自己未满 13 岁时使用
- 限制跟踪:限制数据收集,以遵守儿童隐私保护法
有关完整的方法文档,请参阅trackingUnder13 参考资料。
实施示例
通过年龄验证确定用户未满 13 岁后,立即调用trackingUnder13() 。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'package:shared_preferences/shared_preferences.dart';
class COPPAManager extends StatefulWidget {
@override
_COPPAManagerState createState() => _COPPAManagerState();
}
class _COPPAManagerState extends State<COPPAManager> {
final TextEditingController ageController = TextEditingController();
// User identified as under 13
Future<void> onUserUnder13() async {
Singular.trackingUnder13();
print('COPPA mode enabled for user under 13');
// Store age category for app restart
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user_age_category', 'under_13');
}
// Call after age verification
Future<void> onAgeVerified(int userAge) async {
final prefs = await SharedPreferences.getInstance();
if (userAge < 13) {
Singular.trackingUnder13();
print('COPPA restrictions applied');
// Also limit advertising identifiers
Singular.limitDataSharing(true);
await prefs.setString('user_age_category', 'under_13');
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Child Account'),
content: Text('Special privacy protections have been applied.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
} else {
await prefs.setString('user_age_category', 'adult');
print('Adult account - standard tracking');
}
}
void handleAgeSubmit() {
final userAge = int.tryParse(ageController.text);
if (userAge == null || userAge < 1 || userAge > 120) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Invalid Age'),
content: Text('Please enter a valid age.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
return;
}
onAgeVerified(userAge);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: ageController,
decoration: InputDecoration(
hintText: 'Enter your age',
),
keyboardType: TextInputType.number,
),
ElevatedButton(
onPressed: handleAgeSubmit,
child: Text('Submit'),
),
],
);
}
@override
void dispose() {
ageController.dispose();
super.dispose();
}
}
重要:在确定用户未满 13 岁后,应尽早调用此方法,最好是在应用程序初始化期间或年龄验证后立即调用。这样可确保所有后续跟踪都遵守儿童隐私法规。
限制广告标识符配置
在 SDK 初始化期间,限制收集和使用服务于儿童的应用程序的广告标识符(Android 上为 GAID,iOS 上为 IDFA)。
配置属性:
bool? limitAdvertisingIdentifiers
参数:
- true:启用限制广告标识符模式(限制收集
- false:禁用限制广告标识符模式:禁用有限广告标识符模式(正常收集
使用案例:
- 儿童应用程序:主要为 13 岁以下用户设计的应用程序
- 混合受众应用程序:同时为成人和儿童服务的应用程序,年龄在 SDK 初始化前确定
- 隐私优先应用程序:从一开始就应用广告标识符限制
有关完整的配置文档,请参阅limitAdvertisingIdentifiers 参考资料。
配置示例
在 SDK 初始化期间为预先知道隐私要求的应用程序设置广告标识符限制。
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
// Limit advertising identifiers at initialization
void initializeSingular() {
final config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
// Enable limited advertising identifiers mode
config.limitAdvertisingIdentifiers = true;
Singular.start(config);
print('SDK initialized with advertising identifier restrictions');
}
组合方法:对于为儿童提供服务的应用程序,将trackingUnder13() 与limitAdvertisingIdentifiers = true 结合使用,以确保全面遵守 COPPA。
实施最佳实践
完整隐私管理示例
实施尊重用户偏好并符合法规的全面隐私控制。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'package:shared_preferences/shared_preferences.dart';
const String PREF_USER_CONSENT = 'privacy_user_consent';
const String PREF_DATA_SHARING = 'privacy_data_sharing';
const String PREF_AGE_CATEGORY = 'user_age_category';
/// Initialize privacy settings on app startup based on stored preferences
Future<void> initializePrivacySettings() async {
final hasUserConsent = await getUserConsent();
final allowDataSharing = await getDataSharingPreference();
final ageCategory = await getAgeCategory();
// Apply age-based restrictions first
if (ageCategory == 'under_13') {
Singular.trackingUnder13();
print('COPPA restrictions applied on startup');
}
// Apply stored tracking preference
if (hasUserConsent) {
Singular.trackingOptIn();
Singular.resumeAllTracking();
print('Privacy initialized: Tracking enabled with consent');
} else {
Singular.stopAllTracking();
print('Privacy initialized: Tracking disabled');
}
// Set data sharing preference (inverse logic)
Singular.limitDataSharing(!allowDataSharing);
print('Privacy initialized: consent=$hasUserConsent, sharing=$allowDataSharing');
}
/// User accepts tracking via consent dialog
Future<void> onUserAcceptedTracking() async {
await saveUserConsent(true);
Singular.trackingOptIn();
Singular.resumeAllTracking();
print('User accepted tracking');
}
/// User declines tracking
Future<void> onUserDeclinedTracking() async {
await saveUserConsent(false);
Singular.stopAllTracking();
print('User declined tracking');
}
/// User updates data sharing preference
Future<void> setDataSharingEnabled(bool enabled) async {
await saveDataSharingPreference(enabled);
// Note: limitDataSharing uses inverse logic
// false = data sharing enabled, true = data sharing limited
Singular.limitDataSharing(!enabled);
print('Data sharing: ${enabled ? 'Enabled' : 'Limited'}');
}
/// Check if tracking is currently enabled
Future<bool> isTrackingEnabled() async {
final isStopped = await Singular.isAllTrackingStopped();
return !isStopped;
}
/// Get current privacy status as readable text
Future<String> getPrivacyStatus() async {
final isEnabled = await isTrackingEnabled();
final dataSharingEnabled = await getDataSharingPreference();
final ageCategory = await getAgeCategory();
String status = 'Tracking: ${isEnabled ? 'Enabled' : 'Disabled'}
';
status += 'Data Sharing: ${dataSharingEnabled ? 'Enabled' : 'Limited'}';
if (ageCategory == 'under_13') {
status += '
COPPA: Restrictions Applied';
}
return status;
}
// Private helper methods for SharedPreferences
Future<bool> getUserConsent() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(PREF_USER_CONSENT) ?? false;
}
Future<void> saveUserConsent(bool consent) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(PREF_USER_CONSENT, consent);
}
Future<bool> getDataSharingPreference() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(PREF_DATA_SHARING) ?? false;
}
Future<void> saveDataSharingPreference(bool enabled) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(PREF_DATA_SHARING, enabled);
}
Future<String?> getAgeCategory() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(PREF_AGE_CATEGORY);
}
// Flutter widget to initialize privacy on app startup
class PrivacyInitializer extends StatefulWidget {
final Widget child;
const PrivacyInitializer({Key? key, required this.child}) : super(key: key);
@override
_PrivacyInitializerState createState() => _PrivacyInitializerState();
}
class _PrivacyInitializerState extends State<PrivacyInitializer> {
@override
void initState() {
super.initState();
initializePrivacySettings();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
关键实施指南
最佳实践:
- 持久存储:使用共享首选项或安全存储解决方案保存用户首选项
- 尽早初始化:尽可能在 SDK 初始化之前应用隐私设置
-
UI 同步:使用
isAllTrackingStopped()保持设置 UI 与实际 SDK 状态同步 - 清晰沟通:在应用设置中提供清晰、易用的隐私控制
-
反向逻辑:请记住,
limitDataSharing(false)意味着启用数据共享,而true则意味着限制数据共享 -
COPPA 优先级:在其他隐私设置之前应用儿童隐私保护 (
trackingUnder13()) - 合规文档:保留用户何时以及如何提供或撤销同意的记录