Flutter SDK
Screen recording and feedback collection SDK for Flutter apps. Capture user sessions, bug reports, and feedback with automatic video upload.
Features
- In-app screen recording with audio
- Automatic upload to your Horus project
- User identification and custom metadata
- Progress tracking and error handling
- iOS Privacy Manifest compliant (iOS 17+)
- Android 15 (API 35) compatible
Installation
Add horus_sdk to your pubspec.yaml:
yaml
dependencies:
horus_sdk: ^1.0.0Then run:
bash
flutter pub getPlatform Setup
iOS
Add the following to your Info.plist:
xml
<!-- Required for microphone audio -->
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access to record audio with your screen recording.</string>Android
Add these permissions to your AndroidManifest.xml:
xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />Quick Start
dart
import 'package:horus_sdk/horus_sdk.dart';
// 1. Initialize the SDK (typically in main() or app startup)
final sdk = HorusSdk();
await sdk.initialize(HorusConfig(
embedToken: 'proj_your_token_here',
userInfo: HorusUserInfo(
email: 'user@example.com',
name: 'John Doe',
),
));
// 2. Start recording
await sdk.startRecording();
// 3. Stop and upload
await sdk.stopRecording(
description: 'Bug report: Login button not working',
metadata: {
'screen': 'LoginScreen',
'appVersion': '1.2.3',
},
);Event Handling
Listen to SDK events for UI updates:
dart
final sdk = HorusSdk();
// Recording started
sdk.onRecordingStarted.listen((_) {
setState(() => _isRecording = true);
});
// Recording stopped
sdk.onRecordingStopped.listen((duration) {
print('Recorded ${duration.toStringAsFixed(1)} seconds');
});
// Upload progress
sdk.onUploadProgress.listen((progress) {
setState(() => _uploadProgress = progress);
});
// Upload complete
sdk.onUploadComplete.listen((result) {
setState(() => _isRecording = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Recording uploaded!')),
);
});
// Error handling with pattern matching
sdk.onError.listen((error) {
setState(() => _isRecording = false);
switch (error) {
case PermissionDenied(:final message):
_showPermissionDialog();
case UploadFailed(:final message):
_showRetryButton(message);
case RecordingFailed(:final message):
_showErrorSnackbar(message);
default:
_showErrorSnackbar(error.message);
}
});Configuration Options
dart
HorusConfig(
// Required: Your project's embed token (from Horus dashboard)
embedToken: 'proj_...',
// Optional: API URL (defaults to production)
apiUrl: 'https://tryhorus.io',
// Optional: User identification
userInfo: HorusUserInfo(
email: 'user@example.com',
name: 'John Doe',
userId: 'user_123',
),
// Optional: Custom metadata attached to all recordings
customMetadata: {
'appVersion': '1.2.3',
'environment': 'production',
},
)Updating User Info
If the user logs in after initialization:
dart
await sdk.setUserInfo(HorusUserInfo(
email: user.email,
name: user.displayName,
userId: user.id,
));API Reference
HorusSdk
| Method | Description |
|---|---|
initialize(config) | Initialize the SDK with configuration |
startRecording({includeAudio}) | Start in-app screen recording |
stopRecording({description, metadata}) | Stop recording and upload |
cancelRecording() | Cancel recording without uploading |
setUserInfo(userInfo) | Update user information |
isInitialized() | Check if SDK is initialized |
isRecording() | Check if recording is in progress |
getRecordingDuration() | Get current recording duration |
reset() | Reset SDK state |
Events
| Stream | Emits |
|---|---|
onRecordingStarted | When recording starts |
onRecordingStopped | Recording duration when stopped |
onUploadProgress | Upload progress (0.0 - 1.0) |
onUploadComplete | UploadResult on success |
onError | HorusError on failure |
Error Types
Use Dart 3 pattern matching for exhaustive error handling:
dart
switch (error) {
case NotInitialized(:final message):
// SDK not initialized
case PermissionDenied(:final message):
// User denied recording permission
case RecordingFailed(:final message):
// Recording capture failed
case UploadFailed(:final message):
// Upload to server failed
case NetworkError(:final message):
// Network connectivity issue
case InvalidConfig(:final message):
// Configuration validation failed
}Compliance
iOS Privacy Manifest
This SDK includes a PrivacyInfo.xcprivacy file that declares:
- Collected Data Types: Screen recording data, audio data, device identifiers
- Accessed APIs: System boot time, disk space, file timestamps
- Tracking: Not used for tracking (NSPrivacyTracking = false)
Android API 35
This SDK targets Android API 35 and is compatible with Android 15's 16KB page size requirements.
Troubleshooting
Recording fails to start
- Check that screen recording permissions are granted
- Ensure the app has microphone permission (if audio is enabled)
- Verify the device supports screen recording
Upload fails
- Verify network connectivity
- Check that the
embedTokenis valid - Ensure sufficient storage space
No audio in recording
- Request microphone permission first
- Ensure
includeAudio: trueis passed (default) - Check that no other app is using the microphone
Support
- Documentation: docs.tryhorus.io/flutter
- Issues: GitHub Issues
- Email: support@tryhorus.io