securiti_consent_sdk 1.130.0
securiti_consent_sdk: ^1.130.0 copied to clipboard
A Flutter plugin for managing user consent preferences and compliance with privacy regulations. Integrates with Securiti's Consent Management Platform.
example/lib/main.dart
import 'package:consent_sdk_plugin/cmp_sdk_options.dart';
import 'package:consent_sdk_plugin/models/cmp_sdk_logger_level.dart';
import 'package:consent_sdk_plugin/models/consent_status.dart';
import 'package:consent_sdk_plugin/models/post_consents_request.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:io' show Platform;
import 'package:flutter/services.dart';
import 'package:consent_sdk_plugin/consent_sdk_plugin.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String _latestResult = 'No method called yet';
static const EventChannel _eventChannel = EventChannel('ai.securiti.consent_sdk_plugin/isSDKReady');
final _consentSdkPlugin = ConsentSdkPlugin();
bool _isSDKReady = false;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
initPlatformState();
String appURL = 'your iOS App URL';
String cdnURL = 'Your iOS CDN URL';
String tenantID = 'Your iOS Tenant ID';
String appID = 'Your iOS App ID';
String locationCode = 'iOS Location Code';
if (Platform.isAndroid) {
appURL = 'your Android App URL';
cdnURL = 'Your Android CDN URL';
appID = 'Your Android App ID';
locationCode = 'Android Location Code';
}
CmpSDKOptions options = CmpSDKOptions(
appURL: appURL,
cdnURL: cdnURL,
tenantID: tenantID,
appID: appID,
testingMode: true,
loggerLevel: CmpSDKLoggerLevel.debug,
consentsCheckInterval: 60,
subjectId: 'flutterSubject',
languageCode: 'en',
locationCode: locationCode,
);
_consentSdkPlugin.setupSDK(options.toMap());
_eventChannel.receiveBroadcastStream().listen((event) {
setState(() {
_isSDKReady = event as bool;
if (_isSDKReady) {
_consentSdkPlugin.presentConsentBanner();
}
});
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _consentSdkPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
// Convert any Dart object to a JSON-formatted string efficiently
String _formatJsonForDisplay(dynamic obj, {int depth = 0, int maxDepth = 3}) {
// Limit recursion depth for performance
if (depth > maxDepth) {
return obj.toString();
}
String indent = ' ' * depth;
String childIndent = ' ' * (depth + 1);
if (obj == null) {
return 'null';
} else if (obj is Map) {
if (obj.isEmpty) return '{}';
List<String> pairs = [];
obj.forEach((key, value) {
String formattedValue = _formatJsonForDisplay(value, depth: depth + 1, maxDepth: maxDepth);
pairs.add('$childIndent"$key": $formattedValue');
});
return '{\n${pairs.join(',\n')}\n$indent}';
} else if (obj is List) {
if (obj.isEmpty) return '[]';
List<String> items = [];
for (var item in obj) {
items.add('$childIndent${_formatJsonForDisplay(item, depth: depth + 1, maxDepth: maxDepth)}');
}
return '[\n${items.join(',\n')}\n$indent]';
} else if (obj is String) {
// Escape quotes and use proper JSON string formatting
return '"${obj.replaceAll('"', '\\"')}"';
} else if (obj is num || obj is bool) {
// Numbers and booleans can be represented directly
return obj.toString();
} else {
// Try to use toJson if available, or default to toString()
try {
if (obj is dynamic && obj.toJson != null) {
return _formatJsonForDisplay(obj.toJson(), depth: depth, maxDepth: maxDepth);
}
} catch (_) {}
// Last resort
return '"${obj.toString().replaceAll('"', '\\"')}"';
}
}
void _updateResult(String methodName, dynamic result) {
// If it's a list, split it into smaller chunks for displaying
if (result is List && result.length > 0) {
// Special handling for complex collections
List<Map<String, dynamic>> formattedItems = [];
for (var i = 0; i < result.length; i++) {
var item = result[i];
// Create a simplified format with special handling for models
if (item is dynamic && item.toJson != null) {
try {
formattedItems.add(item.toJson());
continue;
} catch (_) {}
}
if (item is Map) {
formattedItems.add(Map<String, dynamic>.from(item));
} else {
// Wrap non-map items in a simple container
formattedItems.add({'value': item.toString()});
}
}
// Apply JSON formatting for the entire list
final jsonOutput = _formatJsonForDisplay(formattedItems);
setState(() {
_latestResult = "$methodName result: (${result.length} items)\n$jsonOutput";
});
}
// If result has toJson method, use that
else if (result != null && result is dynamic) {
try {
if (result.toJson != null) {
final jsonOutput = _formatJsonForDisplay(result.toJson());
setState(() {
_latestResult = "$methodName result:\n$jsonOutput";
});
return;
}
} catch (_) {}
// Fall back to generic JSON formatter
final jsonOutput = _formatJsonForDisplay(result);
setState(() {
_latestResult = "$methodName result:\n$jsonOutput";
});
}
else {
// For null or simple types
setState(() {
_latestResult = "$methodName result:\n${result ?? 'null'}";
});
}
// No need to log the entire output which can be very large
developer.log("$methodName executed successfully");
}
Future<void> _getBannerConfig() async {
try {
final config = await _consentSdkPlugin.getBannerConfig();
developer.log("getBannerConfig result type: ${config.runtimeType}");
if (config == null) {
_updateResult('getBannerConfig', 'null - No banner config available');
return;
}
// Use the complete BannerConfig model with toJson
_updateResult('getBannerConfig', config);
} catch (e) {
_updateResult('getBannerConfig', 'Error: $e');
}
}
Future<void> _getConsentByPurposeId() async {
try {
// First, get the list of purposes to find a valid purposeId
final purposes = await _consentSdkPlugin.getPurposes();
if (purposes.isEmpty) {
_updateResult('getConsentByPurposeId', 'No purposes available to check consent');
return;
}
// Get the first purpose ID
final purposeId = purposes.first.purposeId;
final purposeName = purposes.first.purposeName?['en'] ?? 'Unknown';
developer.log("Getting consent for purpose ID: $purposeId");
final status = await _consentSdkPlugin.getConsent(purposeId ?? 1);
developer.log("getConsentByPurposeId result type: ${status.runtimeType}");
// Format result with full purpose details
final result = {
'purpose_id': purposeId,
'purpose_name': purposeName,
'consent_status': status.value,
'enum_value': status.toString(),
'purpose_details': purposes.first.toJson(),
};
_updateResult('getConsentByPurposeId', result);
} catch (e) {
_updateResult('getConsentByPurposeId', 'Error: $e');
}
}
Future<void> _getConsentByPermissionId() async {
try {
// First, get the list of permissions to find a valid permissionId
final permissions = await _consentSdkPlugin.getPermissions();
if (permissions.isEmpty) {
_updateResult('getConsentByPermissionId', 'No permissions available to check consent');
return;
}
// Get the first permission ID or use a standard Android permission
final permission = permissions.first;
final permissionId = permission.permissionId ?? "android.permission.CAMERA";
final permissionName = permission.name ?? 'Unknown';
developer.log("Getting consent for permission ID: $permissionId");
final status = await _consentSdkPlugin.getConsent(permissionId);
developer.log("getConsentByPermissionId result type: ${status.runtimeType}");
// Format result with full permission details
final result = {
'permission_id': permissionId,
'permission_name': permissionName,
'consent_status': status.value,
'enum_value': status.toString(),
'permission_details': permission.toJson(),
};
_updateResult('getConsentByPermissionId', result);
} catch (e) {
_updateResult('getConsentByPermissionId', 'Error: $e');
}
}
Future<void> _getPermissions() async {
try {
final permissions = await _consentSdkPlugin.getPermissions();
// Pass the full permissions list directly
_updateResult('getPermissions', permissions);
} catch (e) {
_updateResult('getPermissions', 'Error: $e');
}
}
Future<void> _getPurposes() async {
try {
final purposes = await _consentSdkPlugin.getPurposes();
developer.log("getPurposes result type: ${purposes.runtimeType}, isEmpty: ${purposes.isEmpty}");
if (!purposes.isEmpty) {
developer.log("First purpose type: ${purposes.first.runtimeType}");
}
// Pass the full purposes list directly
_updateResult('getPurposes', purposes);
} catch (e) {
_updateResult('getPurposes', 'Error: $e');
}
}
Future<void> _getSdksInPurpose() async {
try {
// First, get purposes to find a valid ID
final purposes = await _consentSdkPlugin.getPurposes();
if (purposes.isEmpty) {
_updateResult('getSdksInPurpose', 'No purposes available to check SDKs');
return;
}
// Get the first purpose ID or use default 1
final purposeId = purposes.first.purposeId ?? 1;
developer.log("Getting SDKs for purpose ID: $purposeId");
final sdks = await _consentSdkPlugin.getSdksInPurpose(purposeId);
// Format the result with full SDK details
final formattedResult = {
'purpose_id': purposeId,
'purpose_name': purposes.first.purposeName?['en'] ?? 'Unknown',
'sdks_count': sdks.length,
'sdks': sdks,
};
_updateResult('getSdksInPurpose', formattedResult);
} catch (e) {
_updateResult('getSdksInPurpose', 'Error: $e');
}
}
Future<void> _getSettingsPrompt() async {
try {
final settingsPrompt = await _consentSdkPlugin.getSettingsPrompt();
if (settingsPrompt == null) {
_updateResult('getSettingsPrompt', 'null - No settings prompt available');
return;
}
// Settings prompt will be automatically formatted through toJson
_updateResult('getSettingsPrompt', settingsPrompt);
} catch (e) {
_updateResult('getSettingsPrompt', 'Error: $e');
}
}
Future<void> _setConsentForPurpose() async {
try {
// First, get the list of purposes to find a valid purposeId
final purposes = await _consentSdkPlugin.getPurposes();
if (purposes.isEmpty) {
_updateResult('setConsentForPurpose', 'No purposes available to set consent');
return;
}
// Get the first purpose ID
final purposeId = purposes.first.purposeId;
developer.log("Setting consent for purpose ID: $purposeId");
await _consentSdkPlugin.setConsent(purposeId ?? 1, ConsentStatus.granted);
_updateResult('setConsentForPurpose', 'Consent set to GRANTED for purpose ID: $purposeId');
} catch (e) {
_updateResult('setConsentForPurpose', 'Error: $e');
}
}
Future<void> _setConsentForPermission() async {
try {
// First, get the list of permissions to find a valid permissionId
final permissions = await _consentSdkPlugin.getPermissions();
if (permissions.isEmpty) {
_updateResult('setConsentForPermission', 'No permissions available to set consent');
return;
}
// Get the first permission ID or use a standard Android permission
final permissionId = permissions.first.permissionId ?? "android.permission.CAMERA";
developer.log("Setting consent for permission ID: $permissionId");
await _consentSdkPlugin.setConsent(permissionId, ConsentStatus.granted);
_updateResult('setConsentForPermission', 'Consent set to GRANTED for permission ID: $permissionId');
} catch (e) {
_updateResult('setConsentForPermission', 'Error: $e');
}
}
Future<void> _resetConsents() async {
try {
await _consentSdkPlugin.resetConsents();
_updateResult('resetConsents', 'Consents reset successfully');
} catch (e) {
_updateResult('resetConsents', 'Error: $e');
}
}
Future<void> _getAllConsents() async {
try {
// Get all purposes and their consent statuses
final purposes = await _consentSdkPlugin.getPurposes();
// Collect full purpose data with consent status
List<Map<String, dynamic>> purposeData = [];
// Limit to first 5 for performance
int count = purposes.length > 5 ? 5 : purposes.length;
for (var i = 0; i < count; i++) {
var purpose = purposes[i];
if (purpose.purposeId != null) {
final consent = await _consentSdkPlugin.getConsent(purpose.purposeId!);
final purposeName = purpose.purposeName?['en'] ?? 'Unknown';
purposeData.add({
'purpose_id': purpose.purposeId,
'name': purposeName,
'consent_status': consent.value,
'purpose_data': purpose.toJson() // Include full purpose data
});
}
}
// Get all permissions and their consent statuses
final permissions = await _consentSdkPlugin.getPermissions();
List<Map<String, dynamic>> permissionData = [];
// Limit to first 5 for performance
count = permissions.length > 5 ? 5 : permissions.length;
for (var i = 0; i < count; i++) {
var permission = permissions[i];
if (permission.permissionId != null) {
final consent = await _consentSdkPlugin.getConsent(permission.permissionId!);
final permissionName = permission.name ?? 'Unknown';
permissionData.add({
'permission_id': permission.permissionId,
'name': permissionName,
'consent_status': consent.value,
'permission_data': permission.toJson() // Include full permission data
});
}
}
// Format result as JSON with all available details
final result = {
'total_purposes': purposes.length,
'total_permissions': permissions.length,
'purposes': purposeData,
'permissions': permissionData,
};
_updateResult('getAllConsents', result);
} catch (e) {
_updateResult('getAllConsents', 'Error: $e');
}
}
Widget _buildMethodButton(String label, VoidCallback onPressed) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: ElevatedButton(
onPressed: _isSDKReady ? onPressed : null,
child: Text(label),
),
);
}
Widget _buildSDKMethodsList() {
return Expanded(
child: SingleChildScrollView(
controller: _scrollController,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// UI Methods
const Padding(
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Text('UI Methods', style: TextStyle(fontWeight: FontWeight.bold)),
),
_buildMethodButton('Present Consent Banner', () {
_consentSdkPlugin.presentConsentBanner();
_updateResult('presentConsentBanner', 'Banner presented');
}),
_buildMethodButton('Open Preference Center', () {
_consentSdkPlugin.presentPreferenceCenter();
_updateResult('presentPreferenceCenter', 'Preference center opened');
}),
// Get Methods
const Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Text('Get Data Methods', style: TextStyle(fontWeight: FontWeight.bold)),
),
_buildMethodButton('Get Banner Config', _getBannerConfig),
_buildMethodButton('Get Permissions', _getPermissions),
_buildMethodButton('Get Purposes', _getPurposes),
_buildMethodButton('Get SDKs in Purpose', _getSdksInPurpose),
_buildMethodButton('Get Settings Prompt', _getSettingsPrompt),
// Consent Management Methods
const Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Text('Consent Management', style: TextStyle(fontWeight: FontWeight.bold)),
),
_buildMethodButton('Get Consent by Purpose ID', _getConsentByPurposeId),
_buildMethodButton('Get Consent by Permission ID', _getConsentByPermissionId),
_buildMethodButton('Get All Consents', _getAllConsents),
_buildMethodButton('Set Consent for Purpose', _setConsentForPurpose),
_buildMethodButton('Set Consent for Permission', _setConsentForPermission),
_buildMethodButton('Reset All Consents', _resetConsents),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Consent SDK Example'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Securiti\'s SDK is ${_isSDKReady ? "ready" : "not ready"}',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _isSDKReady ? Colors.green : Colors.red,
),
),
const SizedBox(height: 10),
Text('Platform: $_platformVersion'),
const Divider(),
],
),
),
// List of SDK methods
_buildSDKMethodsList(),
// Results display with performance optimizations
Container(
height: 300, // Double the height to show more content
margin: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
// Use a ListView builder for optimal performance with large text
child: Scrollbar(
child: ListView.builder(
itemCount: 1,
itemBuilder: (context, index) {
return SelectableText(
_latestResult,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 11.0, // Smaller font for better performance
height: 1.2, // Tighter line height
),
);
},
),
),
),
],
),
),
);
}
}