An open API service indexing awesome lists of open source software.

https://github.com/synheart-ai/synheart-behavior-flutter

Privacy-preserving Flutter SDK for behavioral signals โ€” typing, motion, attention
https://github.com/synheart-ai/synheart-behavior-flutter

behavior dart digital-phenotyping flutter mental-health mobile-sensing privacy sdk

Last synced: 19 days ago
JSON representation

Privacy-preserving Flutter SDK for behavioral signals โ€” typing, motion, attention

Awesome Lists containing this project

README

          

๐ŸŒ [synheart.ai](https://synheart.ai) โ€” Human State Interface (HSI) infrastructure for developers and AI systems.

# Synheart Behavior

> **Source-available.** This repository is open for reading, auditing, and
> filing issues. We do **not** accept pull requests โ€” see
> [CONTRIBUTING.md](CONTRIBUTING.md) for the rationale and how to contribute
> via issues. Security reports go through [SECURITY.md](SECURITY.md).
> On-device behavioral signal inference from digital interactions for Flutter applications

[![CI](https://github.com/synheart-ai/synheart-behavior-flutter/actions/workflows/ci.yml/badge.svg)](https://github.com/synheart-ai/synheart-behavior-flutter/actions/workflows/ci.yml)
[![pub.dev](https://img.shields.io/pub/v/synheart_behavior.svg)](https://pub.dev/packages/synheart_behavior)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android-lightgrey.svg)](https://pub.dev/packages/synheart_behavior)
[![Dart](https://img.shields.io/badge/dart-%3E%3D3.0.0-blue.svg)](https://dart.dev/)
[![Flutter](https://img.shields.io/badge/flutter-%3E%3D3.10.0-blue.svg)](https://flutter.dev/)

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
- **On-Demand Metrics**: Calculate behavioral metrics for custom time ranges within sessions
- **Raw motion forwarding (optional)**: 50 Hz accelerometer batches surfaced via `onMotionSample` for downstream consumers. The SDK is the *collector*, not the classifier.
- **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

## ๐Ÿ“ฆ Installation

Add to your `pubspec.yaml`:

```yaml
dependencies:
synheart_behavior: ^0.4.0
```

Then run:

```bash
flutter pub get
```

### Platform Setup

This plugin ships as a standard Flutter plugin with native iOS/Android implementations. No additional native binaries are required for basic behavioral metrics.

When `emitRawMotionSamples` is on, the SDK forwards raw 50 Hz accelerometer batches via `onMotionSample`; downstream consumers (e.g. the Synheart Core SDK) feed those samples to the on-device motion classifier.

For optional features (notifications and calls), see the [Permissions](#-permissions) section below.

## ๐ŸŽฏ Quick Start

Here's a complete example to get you started:

```dart
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 createState() => _HomePageState();
}

class _HomePageState extends State {
BehaviorSession? _session;
StreamSubscription? _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 _startSession() async {
try {
_session = await widget.behavior.startSession();
print('Session started: ${_session!.sessionId}');
} catch (e) {
print('Failed to start session: $e');
}
}

Future _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

1. **Initialize the SDK** - Call `SynheartBehavior.initialize()` before using the SDK
2. **Wrap Your App** - Use `wrapWithGestureDetector()` to enable gesture tracking
3. **Listen to Events** - Subscribe to `onEvent` stream for real-time behavioral signals
4. **Track Sessions** - Start and end sessions to get behavioral summaries
5. **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:

```dart
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

`BehaviorEventType` has **eight canonical values**:
`scroll, tap, swipe, app_switch, notification, call, typing,
clipboard`.

- **scroll**: Velocity, acceleration, direction, direction reversals
- **tap**: Duration, long-press detection
- **swipe**: Direction, distance, velocity, acceleration
- **app_switch**: Foreground/background transitions, used for task-switch metrics
- **notification**: Received, opened, ignored (requires permission)
- **call**: Answered, ignored, dismissed (requires permission)
- **typing**: Speed, cadence, gap ratio, backspace count (no content)
- **clipboard**: Copy / paste / cut event counts (no content)

> This package is the **collector**. Higher-level behavioral metrics
> (focus hint, distraction score, burstiness, etc.) are computed by
> the Synheart Runtime when these events are fed into Synheart Core.

Each event includes:

- `eventId`: Unique identifier
- `sessionId`: Associated session ID
- `timestamp`: ISO 8601 timestamp
- `eventType`: 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

```dart
// 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)

```dart
// 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:

```dart
final config = BehaviorConfig(
// Enable/disable signal types
enableInputSignals: true, // Scroll, tap, swipe gestures
enableAttentionSignals: true, // App switching, idle gaps, session stability
enableMotionLite: true, // Lightweight on-device motion classification
emitRawMotionSamples: true, // Forward raw 50 Hz accel batches via onMotionSample

// 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:

```dart
// Disable motion tracking to save battery
await behavior.updateConfig(BehaviorConfig(
enableInputSignals: true,
enableAttentionSignals: true,
enableMotionLite: false, // Disabled
));
```

## ๐Ÿ“ˆ Session Management

### Starting a Session

```dart
// 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:

```dart
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.behavioralDistractionScore}');
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}');

// Motion state classification is no longer surfaced on the
// session summary; subscribe to onMotionSample to forward raw
// accelerometer batches to a downstream classifier.
```

### On-Demand Metrics Calculation

Calculate behavioral metrics for a custom time range within a session:

```dart
// Calculate metrics for a specific time range
final metrics = await behavior.calculateMetricsForTimeRange(
startTimestampSeconds: 1767688063, // Unix timestamp in seconds
endTimestampSeconds: 1767688130, // Unix timestamp in seconds
sessionId: 'SESS-1767688063415', // Optional: session ID (uses current if not provided)
);

// Access the calculated metrics
print('Total events: ${metrics['activity_summary']['total_events']}');
print('App switches: ${metrics['activity_summary']['app_switch_count']}');
print('Interaction intensity: ${metrics['behavioral_metrics']['interaction_intensity']}');
print('Distraction score: ${metrics['behavioral_metrics']['behavioral_distraction_score']}');

// Motion state (if motion data is available)
if (metrics['motion_state'] != null) {
print('Motion state: ${metrics['motion_state']['major_state']}');
print('Confidence: ${metrics['motion_state']['confidence']}');
}
```

**Note**: The time range must be within the session's start and end times. The SDK validates this automatically and will throw an error if the range is out of bounds.

### Current Statistics

Get real-time statistics without ending a session:

```dart
final stats = await behavior.getCurrentStats();
print('Total events: ${stats.totalEvents}');
print('Active sessions: ${stats.activeSessions}');
```

### Session Status

```dart
// 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 engagement
- `behavioralDistractionScore`: Behavioral proxy for distraction (0-1)
- `focusHint`: Behavioral proxy for focus quality (0-1)
- `deepFocusBlocks`: Periods of sustained, uninterrupted engagement
- `taskSwitchRate`: Frequency of app switching
- `idleTimeRatio`: Proportion of idle time vs active interaction
- `fragmentedIdleRatio`: Ratio of fragmented vs continuous idle periods
- `burstiness`: Temporal clustering of interaction events
- `notificationLoad`: Notification pressure and response patterns
- `scrollJitterRate`: Scroll pattern irregularity

Typing session summary (when available) also includes:

- `correctionRate`: Proportion of correction actions (backspace/delete) relative to typing taps and corrections.
- `clipboardActivityRate`: Proportion of clipboard actions (copy, paste, cut) relative to typing taps and clipboard actions.

All metrics are bounded, normalized, and numerically stable.

## ๐Ÿงฎ Compute boundary

This SDK is an **event producer**. It emits behavioral events, raw counts,
and a small set of cheap real-time stats (`scrollVelocity`, `tapRate`,
`stabilityIndex`, `fragmentationIndex`, โ€ฆ). Per-session aggregates and
ML-scored fields are computed downstream by a runtime / consumer that
subscribes to the event stream โ€” not on-device here.

Concretely on `BehaviorSessionSummary`:

- **Computed in the SDK**: `sessionId`, `startAt`, `endAt`, `microSession`,
`os`, `appId`, `appName`, `sessionSpacing`, `deviceContext`, `systemState`,
`activitySummary`, raw notification/call/clipboard counts.
- **Computed by a downstream consumer (`null` until then)**:
`behavioralMetrics`, `notificationSummary.notificationIgnoreRate`,
`notificationSummary.notificationClusteringIndex`, `typingSessionSummary`,
`behavioralMetrics.behavioralDistractionScore`, `focusHint`,
`deepFocusBlocks`, motion-state classification.

If you use this SDK standalone, expect those fields to be `null`. Wire
the event stream into your aggregator to populate them.

## โš™๏ธ Additional Features

### Text Field Widget

The SDK provides a `BehaviorTextField` widget that tracks typing sessions and emits typing events with full metrics (keystrokes, backspace, copy/paste/cut counts). Text content is never collectedโ€”only timing and action counts.

```dart
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):

```dart
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:

```dart
@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, 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

The SDK is designed around the principles of data minimization,
purpose limitation, user consent, and transparency. See the
[privacy audit](https://docs.synheart.ai/privacy/behavior) for the
detailed static-review notes. This is a self-assessment, not a
third-party certification โ€” legal sufficiency for your specific
deployment depends on how you wire consent in your host app.

The SDK does not track users across apps, does not collect device
identifiers, and does not share data with ad-network brokers. Whether
your host app needs to present an ATT prompt depends on the rest of
the app's behavior, not on this SDK alone.

## ๐Ÿ“ฑ 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: events are processed on background threads, and there
is no persistent storage layer to flush. Specific CPU / memory /
battery numbers are deployment-dependent โ€” earlier docs published
fixed targets, but those were design goals, not measured runtime
numbers. Profile your integration with the platform's standard tools
(Instruments, Android Studio Profiler, Flutter DevTools) for ground
truth on your device class.

## ๐Ÿ—๏ธ Architecture

```text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Your Flutter App โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ SynheartBehavior SDK โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ BehaviorConfig โ”€โ”€โ–บ SynheartBehavior.initialize()โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ–ผ โ–ผ โ–ผ โ”‚ โ”‚
โ”‚ โ”‚ BehaviorGesture Platform Channel MotionSignal โ”‚ โ”‚
โ”‚ โ”‚ Detector (Android/iOS) Collector โ”‚ โ”‚
โ”‚ โ”‚ (scroll, tap, (attention, (raw 50 Hz โ”‚ โ”‚
โ”‚ โ”‚ swipe, typing) notif, call) accel batch) โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ”‚ โ–ผ โ”‚ โ”‚
โ”‚ โ”‚ Stream โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ–ผ โ”‚ โ”‚
โ”‚ โ”‚ BehaviorSession โ”€โ”€โ–บ BehaviorSessionSummary โ”‚ โ”‚
โ”‚ โ”‚ (events, stats, (metrics, typing, โ”‚ โ”‚
โ”‚ โ”‚ windowing) motion state) โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ (passed to synheart-core for HSI ingestion)
```

Signals flow: **Gesture Detector / Platform Channel โ†’ Stream\ โ†’ BehaviorSession โ†’ Summary**.
The SDK never generates HSI directly โ€” it collects and normalizes behavioral signals.

## ๐Ÿงช Testing

```bash
flutter test
dart format --set-exit-if-changed .
flutter analyze
```

Tests are in `test/` covering models (config, event, session, stats), SDK initialization, and platform channel integration.

## ๐Ÿ“‹ 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()` before `runApp()` if initializing in `main()`
- 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 `enableInputSignals` or `enableAttentionSignals` is `true` in 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**:

```bash
cd android
./gradlew clean
cd ..
flutter clean
flutter pub get
```

**iOS**:

```bash
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/`](https://github.com/synheart-ai/synheart-behavior-flutter/tree/main/example) directory.

To run the example:

```bash
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

### SynheartBehavior

```dart
class SynheartBehavior {
static Future initialize({BehaviorConfig? config});
Future dispose();

// Sessions
Future startSession({String? sessionId});
// endSession is called via BehaviorSession.end()
Future getCurrentStats();

// Events
Stream get onEvent;
Future sendEvent(BehaviorEvent event);

// On-demand metrics
Future> calculateMetricsForTimeRange({
required int startTimestampSeconds,
required int endTimestampSeconds,
String? sessionId,
});

// Permissions
Future checkNotificationPermission();
Future requestNotificationPermission();
Future checkCallPermission();
Future requestCallPermission();

// Configuration
Future updateConfig(BehaviorConfig config);
bool get isInitialized;
String? get currentSessionId;

// Flutter widgets
Widget wrapWithGestureDetector(Widget child);
Widget createBehaviorTextField({...});
}
```

### Key Types

| Type | Description |
|---|---|
| `BehaviorConfig` | SDK configuration (signals, batching, consent, motion) |
| `BehaviorEvent` | Single behavioral event with type and payload |
| `BehaviorSession` | Active session with `end()` โ†’ `BehaviorSessionSummary` |
| `BehaviorSessionSummary` | Aggregated metrics (activity, behavioral, typing, motion, notification) |
| `BehaviorStats` | Real-time metrics snapshot (velocity, cadence, tap rate) |
| `BehaviorGestureDetector` | Flutter widget for gesture tracking |
| `BehaviorTextField` | Flutter widget for typing event tracking |
| `MotionSample` | One accelerometer sample in a raw 50 Hz batch |

For full API docs, see the [pub.dev package page](https://pub.dev/packages/synheart_behavior).

## Contributing

This is a source-available repository. Issues and feature requests are
welcome; pull requests are not accepted at this time. See
[CONTRIBUTING.md](CONTRIBUTING.md) for the rationale and the supported
contribution path.

## ๐Ÿ“„ License

Apache 2.0 License - see [LICENSE](LICENSE) file for details.

## ๐Ÿ”— Links

- ๐Ÿ“ฆ [pub.dev package](https://pub.dev/packages/synheart_behavior)
- ๐Ÿ”— [GitHub repository](https://github.com/synheart-ai/synheart-behavior-flutter)
- ๐Ÿ”— [Parent specification repository](https://github.com/synheart-ai/synheart-behavior)
- ๐Ÿ“– [Example App Guide](example/GUIDE.md)
- ๐Ÿ”’ [Privacy audit](https://docs.synheart.ai/privacy/behavior)

## ๐Ÿ”— Related Projects

| Repository | Description |
|---|---|
| [synheart-behavior](https://github.com/synheart-ai/synheart-behavior) | Specification & docs (Source of Truth) |
| [synheart-behavior-kotlin](https://github.com/synheart-ai/synheart-behavior-kotlin) | Android/Kotlin SDK |
| [synheart-behavior-swift](https://github.com/synheart-ai/synheart-behavior-swift) | iOS/Swift SDK |
| [synheart-behavior-chrome](https://github.com/synheart-ai/synheart-behavior-chrome) | Chrome extension |

## 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.

## Not a Medical Device

This SDK is intended for wellness and research use only. It is not a medical device, is not intended to diagnose, treat, cure, or prevent any disease or condition, and has not been evaluated by the FDA or any other regulatory body.