synheart_behavior 0.0.1
synheart_behavior: ^0.0.1 copied to clipboard
A lightweight, privacy-preserving mobile SDK that collects digital behavioral signals from smartphones.
Synheart Behavior #
On-device behavioral signal inference from digital interactions for Flutter applications
A privacy-preserving mobile SDK that collects digital behavioral signals from smartphones. The SDK transforms low-level digital interaction events into structured numerical representations of behavior across event and session. By modeling interaction timing, intensity, fragmentation, and interruption patterns without collecting content or personal data, the SDK provides stable, interpretable metrics to represent digital behavior.
These behavioral signals power downstream systems such as:
- Focus and distraction inference
- Digital wellness analytics
- Cognitive load and fatigue estimation
- Multimodal human state modeling (HSI)
π Features #
- Privacy-First: No text, content, or personally identifiable information (PII) collectedβonly timing-based signals
- Real-Time Streaming: Event streams for scroll, tap, swipe, notification, and call interactions
- Session Tracking: Built-in session management with comprehensive summaries
- Flutter Integration: Gesture detection widgets for Flutter apps
- Minimal Permissions: No permissions required for basic functionality (scroll, tap, swipe). Optional permissions for notification and call tracking.
- Platform Support: iOS and Android with native implementations
π¦ Installation #
Add to your pubspec.yaml:
dependencies:
synheart_behavior: ^0.0.1
Then run:
flutter pub get
Platform Setup #
No additional configuration required! The SDK works out of the box. For optional features (notifications and calls), see the Permissions section below.
π― Quick Start #
Here's a complete example to get you started:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:synheart_behavior/synheart_behavior.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SDK
final behavior = await SynheartBehavior.initialize(
config: const BehaviorConfig(
enableInputSignals: true,
enableAttentionSignals: true,
enableMotionLite: false,
),
);
runApp(MyApp(behavior: behavior));
}
class MyApp extends StatelessWidget {
final SynheartBehavior behavior;
const MyApp({super.key, required this.behavior});
@override
Widget build(BuildContext context) {
return behavior.wrapWithGestureDetector(
MaterialApp(
title: 'My App',
home: HomePage(behavior: behavior),
),
);
}
}
class HomePage extends StatefulWidget {
final SynheartBehavior behavior;
const HomePage({super.key, required this.behavior});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
BehaviorSession? _session;
StreamSubscription<BehaviorEvent>? _eventSubscription;
@override
void initState() {
super.initState();
_startListening();
_startSession();
}
void _startListening() {
// Listen to real-time events
_eventSubscription = widget.behavior.onEvent.listen((event) {
print('Event: ${event.eventType} at ${event.timestamp}');
print('Metrics: ${event.metrics}');
});
}
Future<void> _startSession() async {
try {
_session = await widget.behavior.startSession();
print('Session started: ${_session!.sessionId}');
} catch (e) {
print('Failed to start session: $e');
}
}
Future<void> _endSession() async {
if (_session != null) {
try {
final summary = await _session!.end();
print('Session ended: ${summary.durationMs}ms');
print('Total events: ${summary.activitySummary.totalEvents}');
print('Focus hint: ${summary.behavioralMetrics.focusHint}');
_session = null;
} catch (e) {
print('Failed to end session: $e');
}
}
}
@override
void dispose() {
_eventSubscription?.cancel();
widget.behavior.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My App')),
body: Center(
child: ElevatedButton(
onPressed: _endSession,
child: const Text('End Session'),
),
),
);
}
}
Key Steps #
- Initialize the SDK - Call
SynheartBehavior.initialize()before using the SDK - Wrap Your App - Use
wrapWithGestureDetector()to enable gesture tracking - Listen to Events - Subscribe to
onEventstream for real-time behavioral signals - Track Sessions - Start and end sessions to get behavioral summaries
- Clean Up - Call
dispose()when done to free resources
π‘ Real-Time Event Tracking #
The SDK streams behavioral events in real-time as they occur. This is the primary way to track user behavior:
behavior.onEvent.listen((event) {
print('Event: ${event.eventType} at ${event.timestamp}');
print('Metrics: ${event.metrics}');
// Handle different event types
switch (event.eventType) {
case BehaviorEventType.scroll:
final velocity = event.metrics['velocity'] as double?;
print('Scroll velocity: $velocity px/s');
break;
case BehaviorEventType.tap:
final duration = event.metrics['tap_duration_ms'] as int?;
final longPress = event.metrics['long_press'] as bool?;
print('Tap duration: $duration ms, long press: $longPress');
break;
case BehaviorEventType.swipe:
final direction = event.metrics['direction'] as String?;
final velocity = event.metrics['velocity'] as double?;
print('Swipe direction: $direction, velocity: $velocity px/s');
break;
// ... handle other event types
}
});
π Event Types #
The SDK collects five types of behavioral events:
- Scroll: Velocity, acceleration, direction, direction reversals
- Tap: Duration, long-press detection
- Swipe: Direction, distance, velocity, acceleration
- Notification: Received, opened, ignored (requires permission)
- Call: Answered, ignored, dismissed (requires permission)
Each event includes:
eventId: Unique identifiersessionId: Associated session IDtimestamp: ISO 8601 timestampeventType: Type of event (scroll, tap, swipe, etc.)metrics: Event-specific metrics (velocity, duration, etc.)
π Permissions #
Note: Basic functionality (scroll, tap, swipe) requires no permissions. The following permissions are optional and only needed for notification and call tracking.
No content-level information is ever collected or stored. For notifications, the SDK does not record notification text, sender identity, application source, or semantic meaning. For phone calls, the SDK does not record audio, voice data, call content, or call participants.
Instead, the SDK records only event-level metadata, such as:
- the occurrence of a notification or call,
- the timestamp of the event,
- and the userβs interaction outcome (e.g., opened, dismissed, ignored).
Notification Permission #
Required for tracking notification interactions (received, opened, ignored).
Android: Requires enabling Notification Access in system settings
iOS: Requires notification authorization
// Check if permission is granted
final hasPermission = await behavior.checkNotificationPermission();
if (!hasPermission) {
// Request permission (opens system settings on Android)
final granted = await behavior.requestNotificationPermission();
if (granted) {
print('Notification permission granted');
} else {
print('Notification permission denied');
}
}
Call Permission #
Required for tracking call interactions (answered and ignored).
Android: Requires READ_PHONE_STATE permission
iOS: No explicit permission needed (uses system callbacks)
// Check if permission is granted
final hasPermission = await behavior.checkCallPermission();
if (!hasPermission) {
// Request permission
await behavior.requestCallPermission();
}
π§ Configuration #
Initial Configuration #
Configure the SDK during initialization:
final config = BehaviorConfig(
// Enable/disable signal types
enableInputSignals: true, // Scroll, tap, swipe gestures
enableAttentionSignals: true, // App switching, idle gaps, session stability
enableMotionLite: false, // Device motion (optional, may impact battery)
// Session configuration
sessionIdPrefix: 'MYAPP', // Custom session ID prefix (default: 'SESS')
// User/device identifiers (optional, for custom tracking)
userId: 'user_123', // Optional: custom user identifier
deviceId: 'device_456', // Optional: custom device identifier
// SDK configuration
behaviorVersion: '1.0.0', // SDK version identifier
consentBehavior: true, // Consent flag for behavior tracking
// Advanced settings
eventBatchSize: 10, // Events per batch (default: 10)
maxIdleGapSeconds: 10.0, // Max idle time before task drop (default: 10.0)
);
final behavior = await SynheartBehavior.initialize(config: config);
Update Configuration at Runtime #
You can update the configuration after initialization:
// Disable motion tracking to save battery
await behavior.updateConfig(BehaviorConfig(
enableInputSignals: true,
enableAttentionSignals: true,
enableMotionLite: false, // Disabled
));
π Session Management #
Starting a Session #
// Start with auto-generated session ID
final session = await behavior.startSession();
// Or provide a custom session ID
final session = await behavior.startSession(
sessionId: 'MYAPP-${DateTime.now().millisecondsSinceEpoch}',
);
Ending a Session #
When a session ends, you receive a comprehensive summary:
final summary = await session.end();
// Session metadata
print('Session ID: ${summary.sessionId}');
print('Started: ${summary.startAt}');
print('Ended: ${summary.endAt}');
print('Duration: ${summary.durationMs}ms');
// Behavioral metrics
print('Interaction Intensity: ${summary.behavioralMetrics.interactionIntensity}');
print('Distraction Score: ${summary.behavioralMetrics.distractionScore}');
print('Focus Hint: ${summary.behavioralMetrics.focusHint}');
print('Deep Focus Blocks: ${summary.behavioralMetrics.deepFocusBlocks.length}');
// Activity summary
print('Total Events: ${summary.activitySummary.totalEvents}');
print('App Switches: ${summary.activitySummary.appSwitchCount}');
// Notification summary
print('Notifications: ${summary.notificationSummary.notificationCount}');
print('Ignore Rate: ${summary.notificationSummary.notificationIgnoreRate}');
Current Statistics #
Get real-time statistics without ending a session:
final stats = await behavior.getCurrentStats();
print('Total events: ${stats.totalEvents}');
print('Active sessions: ${stats.activeSessions}');
Session Status #
// Check if SDK is initialized
if (behavior.isInitialized) {
// Check current active session
final currentSessionId = behavior.currentSessionId;
if (currentSessionId != null) {
print('Active session: $currentSessionId');
}
}
Core Behavioral Metrics #
Session-level outputs include:
interactionIntensity: Overall interaction rate and engagementdistractionScore: Behavioral proxy for distraction (0-1)focusHint: Behavioral proxy for focus quality (0-1)deepFocusBlocks: Periods of sustained, uninterrupted engagementtaskSwitchRate: Frequency of app switchingidleRatio: Proportion of idle time vs active interactionfragmentedIdleRatio: Ratio of fragmented vs continuous idle periodsburstiness: Temporal clustering of interaction eventsnotificationLoad: Notification pressure and response patternsscrollJitterRate: Scroll pattern irregularity
All metrics are bounded, normalized, and numerically stable.
βοΈ Additional Features #
Text Field Widget #
The SDK provides a BehaviorTextField widget for convenience. Note that text input interactions are captured as tap events (not separate typing events):
behavior.createBehaviorTextField(
controller: myTextController,
decoration: const InputDecoration(
labelText: 'Enter text',
),
)
Custom Event Sending #
You can manually send events to the SDK. Note that only the predefined event types are supported (scroll, tap, swipe, notification, call):
final event = BehaviorEvent(
eventId: 'custom-event-123',
sessionId: behavior.currentSessionId ?? 'current',
timestamp: DateTime.now(),
eventType: BehaviorEventType.tap, // Use one of the supported event types
metrics: {'customMetric': 42},
);
await behavior.sendEvent(event);
Cleanup #
Always dispose of the SDK when done to free resources:
@override
void dispose() {
behavior.dispose();
super.dispose();
}
π Privacy & Compliance #
The Synheart SDK is designed around privacy-by-design and data minimization principles. It captures only the minimum interaction metadata required to model digital behavior, without accessing personal, semantic, or content-level information.
Hard Guarantees #
β No PII: The SDK does not collect names, contacts, account identifiers, message content, or any user-identifying data. All signals are timing-based and structural.
β No content capture: The SDK does not collect notification text/titles/sender identity, call audio/voice data/participants, or application UI content/screen data.
β No keystroke logging: Text input is never recorded. Interactions with text fields are captured only as abstract tap events (timing and duration only), without any character-level data.
β No audio or visual recording: The SDK does not access the screen buffer, screenshots, camera, microphone, or any form of visual/audio capture.
β Permission-scoped tracking only: Behavioral data is collected exclusively from applications that explicitly receive user permission. The SDK does not monitor, infer, or aggregate behavior across the entire device or across unpermitted applications.
β No tracking across unconsented apps: The SDK only tracks behavior within the app that integrates it and has received user consent.
β Event-level metadata only: Collected data is limited to event type (tap, scroll, swipe, notification, call), timestamp, and non-semantic physical metrics (duration, velocity). No semantic interpretation is performed at the data collection stage.
Connectivity & System Access #
β No internet connectivity required: The SDK functions fully offline and does not require an active internet connection to perform behavioral capture or inference.
β Network availability state only: The SDK may record a binary system-level indicator of whether network connectivity is present at a given time. This signal does not include network traffic, destinations, IPs, or content, does not trigger any data transmission, and is used solely as contextual metadata.
β No Bluetooth or external connectivity required: The SDK does not depend on Bluetooth, NFC, or communication with external devices.
β No background network communication: Behavioral computation and aggregation occur locally without initiating network requests. Any optional data transmission is explicitly controlled, consent-gated, and configurable.
Processing & Storage #
β On-device computation by default: Behavioral features and metrics are computed locally on the device whenever possible, minimizing data exposure.
β Ephemeral data handling: Raw interaction events are processed in-memory and are not persisted in long-term storage unless explicitly configured for research or debugging purposes.
β No third-party data sharing: The SDK does not share raw or derived behavioral data with advertisers, analytics providers, or external third parties.
Regulatory Alignment #
β GDPR / CCPA aligned: The SDK adheres to the principles of data minimization, purpose limitation, user consent, and transparency.
β App Tracking Transparency (ATT) not required: The SDK does not track users across apps, services, or companies and does not perform cross-app or cross-device identification.
π± Platform Support #
- β iOS: Swift 5+, iOS 12.0+
- β Android: Kotlin, API 21+ (Android 5.0+)
- β Flutter: 3.10.0+
β‘ Performance #
The SDK is designed for continuous background operation with minimal resource impact:
- CPU: β€ 1% average
- Memory: β€ 10 MB peak
- Battery: < 0.3% per hour
- Event processing: < 500 ΞΌs per event
- UI blocking: None (all processing on background threads)
π Requirements #
- Dart SDK: >=3.0.0 <4.0.0
- Flutter: >=3.10.0
π Troubleshooting #
SDK Not Initializing #
Problem: SynheartBehavior.initialize() throws an exception.
Solutions:
- Ensure you're calling
WidgetsFlutterBinding.ensureInitialized()beforerunApp()if initializing inmain() - Check that native platform code is properly integrated (should be automatic)
- Verify Flutter version meets requirements (>=3.10.0)
No Events Being Collected #
Problem: onEvent stream is not emitting events.
Solutions:
- Ensure you've wrapped your app with
wrapWithGestureDetector() - Verify a session is started with
startSession() - Check that
enableInputSignalsorenableAttentionSignalsistruein config - For notifications/calls, ensure permissions are granted
Permission Requests Not Working #
Problem: Permission requests don't show dialogs or open settings.
Solutions:
- Android: Notification access requires manual enablement in system settings
- iOS: Ensure you're testing on a real device (simulator may have limitations)
- Check platform-specific permission requirements in your app's manifest/Info.plist
Session End Fails #
Problem: session.end() throws an exception or times out.
Solutions:
- Ensure the session was properly started
- Check that the SDK is still initialized
- Verify native platform channel is working (check logs)
- Try ending the session with a timeout wrapper
Build Errors #
Android:
cd android
./gradlew clean
cd ..
flutter clean
flutter pub get
iOS:
cd ios
pod deintegrate
pod install
cd ..
flutter clean
flutter pub get
π§ͺ Example App #
A complete example app demonstrating all SDK features is available in the example/ directory.
To run the example:
cd example
flutter pub get
flutter run
The example app includes:
- Real-time event visualization
- Session management UI
- Permission handling examples
- Event type handling demonstrations
π API Reference #
For detailed API documentation, see the pub.dev package page.
π€ Contributing #
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
π License #
Apache 2.0 License - see LICENSE file for details.
π₯ Author #
Israel Goytom
π Links #
- π¦ pub.dev package
- π GitHub repository
- π Parent specification repository
- π Example App Guide
π Related Projects #
- Synheart Focus - Cognitive concentration inference
- Synheart Emotion - Physiological emotion inference from biosignals
- Synheart Behavior (Parent) - Multi-platform SDK specification
βοΈ Patent Pending Notice #
This project is provided under an open-source license. Certain underlying systems, methods, and architectures described or implemented herein may be covered by one or more pending patent applications.
Nothing in this repository grants any license, express or implied, to any patents or patent applications, except as provided by the applicable open-source license.