IA Tracking Flutter SDK
A comprehensive Flutter plugin for user action tracking that provides seamless integration with native Android and iOS IA Tracking SDKs. Track user interactions, screen views, navigation, and custom events with high performance and privacy-focused design.
Features
🎯 Comprehensive Tracking
- Screen Views: Automatic and manual screen view tracking
- User Interactions: Button taps, text input, dropdown selections
- Navigation: Route changes, tab switching, deep link navigation
- Search: Query tracking with results count and performance metrics
- Custom Events: Flexible event tracking with properties and metadata
🛡️ Privacy-Focused
- Safe Text Input: Tracks input length only, never actual content
- User Consent: Easy enable/disable functionality for compliance
- Data Control: Complete user data deletion capabilities
- GDPR Ready: Export and deletion APIs for regulatory compliance
⚡ High Performance
- Native Integration: Leverages high-performance native SDKs
- Efficient Storage: SQLite-based local storage with compression
- Smart Batching: Configurable batch sizes for optimal network usage
- Background Processing: Non-blocking async operations
📊 Advanced Analytics
- Session Management: Automatic session handling with configurable timeouts
- Data Retention: Configurable retention policies and automatic cleanup
- Sync Management: Track synced vs unsynced data states
- Statistics: Comprehensive metrics and performance insights
Getting Started
Installation
Add the dependency to your pubspec.yaml file:
dependencies:
ia_tracking: ^1.0.0
Run the installation command:
flutter pub get
Android Setup
Add the native Android SDK dependency to your android/app/build.gradle:
dependencies {
implementation 'com.iav3:ia-tracking-android:1.0.0'
}
iOS Setup
Add the native iOS SDK to your ios/Podfile:
pod 'IATracking', '~> 1.0.0'
Basic Usage
1. Initialize the SDK
import 'package:ia_tracking/ia_tracking.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Configure the SDK
final config = IaTrackingConfig(
apiBaseUrl: 'https://your-api.com/tracking', // REQUIRED: Your API endpoint
// Optional: Cloudflare Access credentials (if your API is protected)
// cfAccessClientId: 'your-cf-client-id',
// cfAccessClientSecret: 'your-cf-secret',
userId: 'user_12345',
appId: 'com.yourapp.id',
apiKey: 'your_api_key_here', // Optional: if your backend requires it
enableAutoFlush: true,
autoFlushIntervalMs: 5000,
privacy: IaPrivacySettings(
trackingEnabled: true,
gdprCompliant: false,
coppaCompliant: false,
),
skanSettings: IaSkanSettings(
enabled: true,
manualConversionManagement: false,
),
);
// Add global properties that will be attached to all events
config.addGlobalProperty('app_version', '1.0.0');
config.addGlobalProperty('environment', 'production');
try {
await IaTracker.instance.initialize(config);
print('IA Tracking initialized successfully');
} catch (e) {
print('Failed to initialize IA Tracking: $e');
}
runApp(MyApp());
}
2. Track Screen Views
class MyScreen extends StatefulWidget {
@override
void initState() {
super.initState();
// Track screen view
IaTracker.instance.trackScreenView('HomeScreen');
}
}
3. Track Button Interactions
ElevatedButton(
onPressed: () async {
// Track button tap with optional coordinates
await IaTracker.instance.trackButtonTap(
'submit_button',
'FormScreen',
coordinates: Offset(100, 200), // Optional
);
// Your button action here
handleSubmit();
},
child: Text('Submit'),
)
4. Track Text Input
TextField(
onChanged: (value) {
// Track input length (privacy-safe, no actual content)
IaTracker.instance.trackTextInput(
'email_field',
'SignupScreen',
inputLength: value.length,
);
},
decoration: InputDecoration(labelText: 'Email'),
)
5. Track Custom Events
await IaTracker.instance.trackCustomEvent(
'product_purchased',
'CheckoutScreen',
elementId: 'buy_now_button',
properties: {
'product_id': '12345',
'product_name': 'Wireless Headphones',
'price': 99.99,
'category': 'electronics',
'payment_method': 'credit_card',
},
);
Advanced Features
Navigation Tracking
// Automatic navigation tracking with Navigator observer
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [
TrackingNavigatorObserver(), // Automatic screen tracking
],
// ... rest of your app
);
}
}
// Manual navigation tracking
await IaTracker.instance.trackNavigation(
'HomeScreen',
'ProductDetailsScreen',
method: 'push',
);
Search Tracking
Future<void> performSearch(String query) async {
final results = await searchService.search(query);
// Track search with results count
await IaTracker.instance.trackSearch(
query,
'SearchScreen',
resultsCount: results.length,
);
}
Session Management
// Start new session (useful after user login)
await IaTracker.instance.startNewSession();
// Update user ID
await IaTracker.instance.setUserId('new_user_id');
// Clear user ID (anonymous tracking)
await IaTracker.instance.setUserId(null);
Enable/Disable Tracking
// Check if tracking is enabled
bool isEnabled = await IaTracker.instance.isEnabled();
// Enable/disable tracking (useful for user privacy settings)
await IaTracker.instance.setEnabled(false); // Disable
await IaTracker.instance.setEnabled(true); // Enable
Data Management
// Get tracking statistics
ActionStatistics stats = await IaTracker.instance.getActionStatistics();
print('Total actions: ${stats.totalActions}');
print('Unsynced actions: ${stats.unsyncedActions}');
// Export unsynced data for server synchronization
List<UserAction> unsyncedActions = await IaTracker.instance.getUnsyncedActions(
limit: 100,
);
// Mark actions as synced after successful upload
List<String> actionIds = unsyncedActions.map((action) => action.id).toList();
await IaTracker.instance.markActionsAsSynced(actionIds);
// Get cleanup statistics
CleanupStats cleanupStats = await IaTracker.instance.getCleanupStats();
print('Database size: ${cleanupStats.estimatedSizeBytes} bytes');
// Perform manual cleanup
await IaTracker.instance.performCleanup();
// Delete all user data (GDPR compliance)
await IaTracker.instance.deleteAllUserData();
// Flush pending operations
await IaTracker.instance.flush();
Configuration Options
IaTrackingConfig
final config = IaTrackingConfig(
// User identification
userId: 'user_12345', // Optional: User identifier
appId: 'com.yourapp.id', // Optional: App identifier
deviceId: 'custom_device_id', // Optional: Custom device ID
// API authentication
apiKey: 'your_api_key', // Optional: API key
// Session settings
sessionTimeoutMinutes: 30, // Default: 30 minutes
// Auto-flush settings
enableAutoFlush: true, // Default: true
autoFlushIntervalMs: 5000, // Default: 5000 (5 seconds)
// Privacy settings
privacy: IaPrivacySettings(
trackingEnabled: true, // Default: true
gdprCompliant: false, // Default: false
coppaCompliant: false, // Default: false
dataSharingEnabled: true, // Default: true
limitDataSharing: false, // Default: false
),
// SKAdNetwork settings (iOS only)
skanSettings: IaSkanSettings(
enabled: true, // Default: true
manualConversionManagement: false, // Default: false
waitForTrackingAuthorizationTimeout: 60, // Default: 60 seconds
),
// Development settings
enableLogging: false, // Default: false
enableDebugMode: false, // Default: false
);
// Add global properties
config.addGlobalProperty('app_version', '1.0.0');
config.addGlobalProperty('environment', 'production');
Data Models
UserAction
All tracked actions are stored as UserAction objects with the following properties:
class UserAction {
final String id; // Unique action identifier
final ActionType actionType; // Type of action (screen_view, button_tap, etc.)
final String? screenName; // Screen where action occurred
final String? elementId; // Element that triggered action
final String? elementType; // Type of element (button, text_field, etc.)
final String? userId; // User who performed action
final String? sessionId; // Session identifier
final DateTime timestamp; // When action occurred
final Map<String, dynamic> properties; // Custom properties
final Map<String, dynamic> deviceInfo; // Device information
final String? appVersion; // App version when action occurred
final String? sdkVersion; // SDK version
final bool isSynced; // Whether action has been synced
final int retryCount; // Number of sync retry attempts
}
ActionType
Supported action types:
enum ActionType {
screenView, // Screen/page views
buttonTap, // Button and clickable element interactions
textInput, // Text field input (length only, privacy-safe)
navigation, // Navigation between screens
search, // Search queries
customEvent, // Custom application events
}
Privacy & Compliance
Data Collection Principles
- Minimal Data: Only collect essential interaction data
- No Sensitive Content: Text input tracking captures length only
- User Control: Easy opt-out mechanisms
- Transparent: Clear documentation of what data is collected
- Secure Storage: Local SQLite database with appropriate permissions
GDPR Compliance
// Data export for user requests
List<UserAction> allUserData = await IaTracker.instance.getUnsyncedActions(
limit: -1, // Get all data
);
// Complete data deletion
await IaTracker.instance.deleteAllUserData();
// Check tracking status
bool isTracking = await IaTracker.instance.isEnabled();
Privacy Settings UI
class PrivacySettingsScreen extends StatefulWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SwitchListTile(
title: Text('Analytics Tracking'),
subtitle: Text('Help improve our app by sharing usage analytics'),
value: _trackingEnabled,
onChanged: (bool value) async {
await IaTracker.instance.setEnabled(value);
setState(() {
_trackingEnabled = value;
});
},
),
ElevatedButton(
onPressed: () async {
// Show confirmation dialog first
await IaTracker.instance.deleteAllUserData();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('All data deleted successfully')),
);
},
child: Text('Delete My Data'),
),
],
),
);
}
}
Error Handling
The SDK is designed to fail gracefully and never interfere with your app's functionality:
try {
await IaTracker.instance.trackButtonTap('button_id', 'ScreenName');
} on InitializationException catch (e) {
print('SDK not initialized: $e');
} on PlatformException catch (e) {
print('Platform error: $e');
} on DatabaseException catch (e) {
print('Database error: $e');
} catch (e) {
print('Unexpected error: $e');
}
Exception Types
InitializationException: SDK not properly initializedInvalidParameterException: Invalid method parametersPlatformException: Platform-specific errorsDatabaseException: Database operation errorsSyncException: Data synchronization errors
Performance Considerations
Best Practices
- Initialize Early: Initialize the SDK in your
main()function - Batch Operations: The SDK automatically batches operations for efficiency
- Background Processing: All operations are performed on background threads
- Memory Efficient: Minimal memory footprint with efficient data structures
- Network Aware: Configurable batch sizes and retry logic
Performance Tips
// Don't track every keystroke - use debouncing
Timer? _debounceTimer;
void onSearchChanged(String query) {
_debounceTimer?.cancel();
_debounceTimer = Timer(Duration(milliseconds: 500), () {
IaTracker.instance.trackSearch(query, 'SearchScreen');
});
}
// Configure auto-flush interval for your app
final config = IaTrackingConfig(
autoFlushIntervalMs: 3000, // More frequent syncing (3 seconds)
// OR
autoFlushIntervalMs: 10000, // Less frequent syncing (10 seconds)
);
// Flush before app shutdown
@override
void dispose() {
IaTracker.instance.flush();
super.dispose();
}
Testing
Unit Testing
// Mock the tracker for unit tests
class MockIaTracker extends Mock implements IaTracker {}
void main() {
group('Tracking Tests', () {
late MockIaTracker mockTracker;
setUp(() {
mockTracker = MockIaTracker();
IaTracker.instance = mockTracker;
});
testWidgets('should track button tap', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
await tester.tap(find.text('Submit'));
verify(mockTracker.trackButtonTap('submit_button', 'TestScreen'))
.called(1);
});
});
}
Integration Testing
void main() {
group('Integration Tests', () {
testWidgets('should initialize and track actions', (tester) async {
final config = IaTrackingConfig(
userId: 'test_user',
enableDebugMode: true,
);
await IaTracker.instance.initialize(config);
await IaTracker.instance.trackScreenView('TestScreen');
final stats = await IaTracker.instance.getActionStatistics();
expect(stats.totalActions, greaterThan(0));
});
});
}
Migration Guide
Latest Breaking Changes
The latest release removes the old TrackingConfiguration class in favor of the more powerful IaTrackingConfig:
// OLD (deprecated)
const config = TrackingConfiguration(
userId: 'user123',
serverUrl: 'https://api.example.com',
debugMode: true,
);
await IaTracker.instance.initialize(config);
// NEW (current)
final config = IaTrackingConfig(
userId: 'user123',
apiKey: 'your_api_key',
enableAutoFlush: true,
privacy: IaPrivacySettings(trackingEnabled: true),
skanSettings: IaSkanSettings(enabled: true),
);
config.addGlobalProperty('app_version', '1.0.0');
await IaTracker.instance.initialize(config);
Troubleshooting
Common Issues
SDK Not Initialized
// Error: InitializationException
// Solution: Ensure initialize() is called before any tracking operations
await IaTracker.instance.initialize(config);
Android Build Issues
// Add to android/app/build.gradle
android {
compileSdkVersion 34
minSdkVersion 21 // Minimum required
}
iOS Build Issues
# Add to ios/Podfile
platform :ios, '12.0' # Minimum required
# Run after adding
cd ios && pod install
Debug Mode
Enable debug mode for detailed logging:
final config = IaTrackingConfig(
enableDebugMode: true, // Enable detailed logging
enableLogging: true, // Enable all logging
);
Logging
The SDK uses different log levels:
- Error: Critical errors that prevent functionality
- Warning: Potential issues that don't break functionality
- Info: General operational information
- Debug: Detailed information for debugging (debug mode only)
Example App
A complete example app is included in the example/ directory demonstrating:
- SDK initialization and configuration
- All tracking methods with real-world usage
- Privacy settings and data management
- Error handling and edge cases
- Performance best practices
Run the example:
cd example
flutter pub get
flutter run
API Reference
IaTracker
The main class for all tracking operations.
Initialization
static IaTracker get instance // Singleton instance
Future<void> initialize(IaTrackingConfig config) // Initialize SDK
Tracking Methods
Future<void> trackScreenView(String screenName)
Future<void> trackButtonTap(String elementId, String screenName, {Offset? coordinates})
Future<void> trackTextInput(String elementId, String screenName, {int? inputLength})
Future<void> trackNavigation(String fromScreen, String toScreen, {String? method})
Future<void> trackSearch(String query, String screenName, {int? resultsCount})
Future<void> trackCustomEvent(String eventName, String screenName, {String? elementId, Map<String, dynamic>? properties})
Session Management
Future<void> startNewSession()
Future<void> setUserId(String? userId)
Control Methods
Future<void> setEnabled(bool enabled)
Future<bool> isEnabled()
Data Methods
Future<ActionStatistics> getActionStatistics()
Future<List<UserAction>> getUnsyncedActions({int limit = 50})
Future<void> markActionsAsSynced(List<String> actionIds)
Future<CleanupStats> getCleanupStats()
Future<void> performCleanup()
Future<void> deleteAllUserData()
Future<void> flush()
Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Development Setup
git clone https://github.com/your-repo/ia-tracking-flutter-sdk.git
cd ia-tracking-flutter-sdk
flutter pub get
cd example && flutter pub get
Running Tests
flutter test
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Documentation: Full API Documentation
- Issues: GitHub Issues
- Email: flutter-support@iav3.com
Changelog
See CHANGELOG.md for a detailed history of changes.
Made with ❤️ by the IAv3 Team