flutter_privacy_shield 0.0.1
flutter_privacy_shield: ^0.0.1 copied to clipboard
Privacy-focused utilities for data anonymization and GDPR compliance
import 'package:flutter/material.dart';
import 'package:flutter_privacy_shield/flutter_privacy_shield.dart';
void main() {
runApp(const PrivacyShieldExampleApp());
}
class PrivacyShieldExampleApp extends StatelessWidget {
const PrivacyShieldExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Privacy Shield Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const PrivacyShieldDemo(),
);
}
}
class PrivacyShieldDemo extends StatefulWidget {
const PrivacyShieldDemo({super.key});
@override
State<PrivacyShieldDemo> createState() => _PrivacyShieldDemoState();
}
class _PrivacyShieldDemoState extends State<PrivacyShieldDemo> {
late PrivacyShield privacyShield;
final TextEditingController _nameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _ssnController = TextEditingController();
Map<String, dynamic>? processedData;
String? privacyScore;
List<Map<String, dynamic>> auditLog = [];
ConsentStatus selectedConsent = ConsentStatus.granted;
RetentionPolicy selectedRetention = RetentionPolicy.mediumTerm;
@override
void initState() {
super.initState();
privacyShield = PrivacyShield();
_updatePrivacyScore();
_updateAuditLog();
}
void _updatePrivacyScore() {
setState(() {
privacyScore = privacyShield.getPrivacyScore().toString();
});
}
void _updateAuditLog() {
setState(() {
auditLog = privacyShield.getAuditLog();
});
}
void _processData() {
if (_nameController.text.isEmpty) {
_showSnackBar('Please enter a name');
return;
}
final userData = {
'name': _nameController.text,
'email': _emailController.text.isNotEmpty ? _emailController.text : null,
'phone': _phoneController.text.isNotEmpty ? _phoneController.text : null,
'ssn': _ssnController.text.isNotEmpty ? _ssnController.text : null,
};
// Remove null values
userData.removeWhere((key, value) => value == null);
try {
final processed = privacyShield.processData(
userData,
purpose: 'demo_processing',
consentStatus: selectedConsent,
retentionPolicy: selectedRetention,
);
setState(() {
processedData = processed;
});
_updateAuditLog();
_showSnackBar('Data processed successfully!');
} catch (e) {
_showSnackBar('Error: ${e.toString()}');
}
}
void _clearData() {
setState(() {
processedData = null;
_nameController.clear();
_emailController.clear();
_phoneController.clear();
_ssnController.clear();
});
}
void _clearAuditLog() {
privacyShield.clearAuditLog();
_updateAuditLog();
_showSnackBar('Audit log cleared');
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Privacy Shield Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildPrivacyScoreCard(),
const SizedBox(height: 16),
_buildInputForm(),
const SizedBox(height: 16),
_buildProcessedDataCard(),
const SizedBox(height: 16),
_buildAuditLogCard(),
],
),
),
);
}
Widget _buildPrivacyScoreCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Privacy Score: $privacyScore/100',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
LinearProgressIndicator(
value: (int.tryParse(privacyScore ?? '0') ?? 0) / 100,
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
(int.tryParse(privacyScore ?? '0') ?? 0) >= 80
? Colors.green
: (int.tryParse(privacyScore ?? '0') ?? 0) >= 60
? Colors.orange
: Colors.red,
),
),
const SizedBox(height: 8),
Text(
'This score indicates how well your data processing meets privacy requirements.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
);
}
Widget _buildInputForm() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Input Data',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name *',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: 'Phone',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _ssnController,
decoration: const InputDecoration(
labelText: 'SSN',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: DropdownButtonFormField<ConsentStatus>(
initialValue: selectedConsent,
decoration: const InputDecoration(
labelText: 'Consent Status',
border: OutlineInputBorder(),
),
items: ConsentStatus.values.map((status) {
return DropdownMenuItem(
value: status,
child: Text(status.toString().split('.').last),
);
}).toList(),
onChanged: (value) {
if (value != null) {
setState(() {
selectedConsent = value;
});
}
},
),
),
const SizedBox(width: 16),
Expanded(
child: DropdownButtonFormField<RetentionPolicy>(
initialValue: selectedRetention,
decoration: const InputDecoration(
labelText: 'Retention Policy',
border: OutlineInputBorder(),
),
items: RetentionPolicy.values.map((policy) {
return DropdownMenuItem(
value: policy,
child: Text(policy.toString().split('.').last),
);
}).toList(),
onChanged: (value) {
if (value != null) {
setState(() {
selectedRetention = value;
});
}
},
),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: _processData,
child: const Text('Process Data'),
),
),
const SizedBox(width: 16),
Expanded(
child: OutlinedButton(
onPressed: _clearData,
child: const Text('Clear'),
),
),
],
),
],
),
),
);
}
Widget _buildProcessedDataCard() {
if (processedData == null) {
return const SizedBox.shrink();
}
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Processed Data',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: processedData!.entries.map((entry) {
if (entry.key == '_privacy_metadata') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(),
Text(
'Privacy Metadata:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
...(entry.value as Map<String, dynamic>)
.entries
.map((meta) {
return Padding(
padding: const EdgeInsets.only(left: 16, bottom: 4),
child: Text('${meta.key}: ${meta.value}'),
);
}),
],
);
}
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text('${entry.key}: ${entry.value}'),
);
}).toList(),
),
),
],
),
),
);
}
Widget _buildAuditLogCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Audit Log (${auditLog.length})',
style: Theme.of(context).textTheme.headlineSmall,
),
if (auditLog.isNotEmpty)
TextButton(
onPressed: _clearAuditLog,
child: const Text('Clear Log'),
),
],
),
const SizedBox(height: 16),
if (auditLog.isEmpty)
const Text('No audit log entries yet.')
else
Container(
height: 200,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
itemCount: auditLog.length,
itemBuilder: (context, index) {
final entry = auditLog[
auditLog.length - 1 - index]; // Show newest first
return ListTile(
title: Text(entry['operation'] ?? 'Unknown'),
subtitle: Text(
'${entry['data_type']} - ${entry['consent_status']}'),
trailing: Text(
entry['timestamp']?.toString().substring(11, 19) ?? '',
style: Theme.of(context).textTheme.bodySmall,
),
);
},
),
),
],
),
),
);
}
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_ssnController.dispose();
super.dispose();
}
}