synheart_wear 0.2.1
synheart_wear: ^0.2.1 copied to clipboard
Unified wearable SDK for Synheart (HR, HRV, steps, energy, stress).
Synheart Wear #
Unified wearable SDK for Flutter — Stream HR, HRV, steps, calories, and distance from Apple Watch, Fitbit, Garmin, Whoop, and Samsung devices with a single, standardized API.
✨ Features #
| Feature | Description |
|---|---|
| 📱 Cross-Platform | iOS & Android support |
| ⌚ Multi-Device | Apple Watch, Fitbit, Garmin, Whoop, Samsung |
| 🔄 Real-Time | Live HR and HRV streaming |
| 📊 Unified Schema | Consistent data format across all devices |
| 🔒 Privacy-First | Consent-based access with encryption |
| 💾 Offline Support | Encrypted local data persistence |
🚀 Quick Start #
Installation #
dependencies:
synheart_wear: ^0.2.1
flutter pub get
Basic Usage #
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:synheart_wear/synheart_wear.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SDK
final adapters = <DeviceAdapter>{
DeviceAdapter.appleHealthKit, // Uses Health Connect on Android
};
final synheart = SynheartWear(
config: SynheartWearConfig.withAdapters(adapters),
);
// Request permissions & initialize
await synheart.requestPermissions(
permissions: {
PermissionType.heartRate,
PermissionType.steps,
PermissionType.calories,
},
reason: 'This app needs access to your health data.',
);
await synheart.initialize();
// Read metrics
final metrics = await synheart.readMetrics();
print('HR: ${metrics.getMetric(MetricType.hr)} bpm');
print('Steps: ${metrics.getMetric(MetricType.steps)}');
}
Real-Time Streaming #
// Stream heart rate every 5 seconds
synheart.streamHR(interval: Duration(seconds: 5))
.listen((metrics) {
final hr = metrics.getMetric(MetricType.hr);
if (hr != null) print('Current HR: $hr bpm');
});
// Stream HRV in 5-second windows
synheart.streamHRV(windowSize: Duration(seconds: 5))
.listen((metrics) {
final hrv = metrics.getMetric(MetricType.hrvRmssd);
if (hrv != null) print('HRV RMSSD: $hrv ms');
});
📊 Data Schema #
All data follows the Synheart Data Schema v1.0:
{
"timestamp": "2025-10-20T18:30:00Z",
"device_id": "applewatch_1234",
"source": "apple_healthkit",
"metrics": {
"hr": 72,
"hrv_rmssd": 45,
"hrv_sdnn": 62,
"steps": 1045,
"calories": 120.4,
"distance": 2.5
},
"meta": {
"battery": 0.82,
"firmware_version": "10.1",
"synced": true
}
}
Access in code:
final metrics = await synheart.readMetrics();
print(metrics.getMetric(MetricType.hr)); // 72
print(metrics.getMetric(MetricType.steps)); // 1045
print(metrics.getMetric(MetricType.distance)); // 2.5
print(metrics.batteryLevel); // 0.82
📚 Full API Documentation | Data Schema Details
⌚ Supported Devices #
| Device | Platform | Status |
|---|---|---|
| Apple Watch | iOS | ✅ Ready |
| Health Connect | Android | ✅ Ready |
| Whoop | iOS/Android | ✅ Ready |
| Fitbit | iOS/Android | 🔄 In Development |
| Garmin | iOS/Android | 🔄 In Development |
| Samsung Watch | Android | 📋 Planned |
⚙️ Platform Configuration #
Android #
Add to android/app/src/main/AndroidManifest.xml:
<!-- Health Connect Permissions -->
<uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE"/>
<uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY"/>
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY"/>
<uses-permission android:name="android.permission.health.READ_STEPS"/>
<uses-permission android:name="android.permission.health.WRITE_STEPS"/>
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"/>
<uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED"/>
<uses-permission android:name="android.permission.health.READ_DISTANCE"/>
<uses-permission android:name="android.permission.health.WRITE_DISTANCE"/>
<!-- Health Connect Package Query -->
<queries>
<package android:name="com.google.android.apps.healthdata" />
<intent>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent>
</queries>
<application>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- Required: Privacy Policy Activity Alias -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity=".MainActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
</application>
Note: MainActivity must extend FlutterFragmentActivity (not FlutterActivity) for Android 14+.
iOS #
Add to ios/Runner/Info.plist:
<key>NSHealthShareUsageDescription</key>
<string>This app needs access to your health data to provide insights.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>This app needs permission to update your health data.</string>
⚠️ Platform Limitations #
| Platform | Limitation | SDK Behavior |
|---|---|---|
| Android | HRV: Only HRV_RMSSD supported |
Automatically maps to supported type |
| Android | Distance: Uses DISTANCE_DELTA |
Automatically uses correct type |
| iOS | Full support for all metrics | No limitations |
🔒 Privacy & Security #
- ✅ Consent-first design
- ✅ AES-256-CBC encryption
- ✅ Automatic key management
- ✅ Anonymized UUIDs
- ✅ Right to forget (revoke & delete)
📖 Additional Resources #
- Full API Documentation — Complete API reference
- GitHub Issues — Report bugs or request features
- pub.dev Package — Package details
📋 Detailed Sections #
Data Schema Details
Field Descriptions #
| Field | Type | Description | Example |
|---|---|---|---|
timestamp |
string (ISO 8601) |
When data was recorded | "2025-10-20T18:30:00Z" |
device_id |
string |
Unique device identifier | "applewatch_1234" |
source |
string |
Data source adapter | "apple_healthkit", "fitbit", "whoop" |
metrics.hr |
number |
Heart rate (bpm) | 72 |
metrics.hrv_rmssd |
number |
HRV RMSSD (ms) | 45 |
metrics.hrv_sdnn |
number |
HRV SDNN (ms) | 62 |
metrics.steps |
number |
Step count | 1045 |
metrics.calories |
number |
Calories (kcal) | 120.4 |
metrics.distance |
number |
Distance (km) | 2.5 |
meta.battery |
number |
Battery level (0.0-1.0) | 0.82 (82%) |
meta.synced |
boolean |
Sync status | true |
Notes:
- Optional fields may be
nullif unavailable - Platform limitations may affect metric availability
metaobject may contain device-specific fields
Platform-Specific Permission Handling
Set<PermissionType> permissions;
if (Platform.isAndroid) {
permissions = {
PermissionType.heartRate,
PermissionType.heartRateVariability, // RMSSD on Android
PermissionType.steps,
PermissionType.calories,
// Distance supported via DISTANCE_DELTA
};
} else {
permissions = {
PermissionType.heartRate,
PermissionType.heartRateVariability,
PermissionType.steps,
PermissionType.calories,
PermissionType.distance,
};
}
final result = await synheart.requestPermissions(
permissions: permissions,
reason: 'This app needs access to your health data.',
);
Usage Examples
Complete Health Monitoring App #
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:synheart_wear/synheart_wear.dart';
class HealthMonitor extends StatefulWidget {
@override
_HealthMonitorState createState() => _HealthMonitorState();
}
class _HealthMonitorState extends State<HealthMonitor> {
late SynheartWear _sdk;
StreamSubscription<WearMetrics>? _hrSubscription;
WearMetrics? _latestMetrics;
bool _isConnected = false;
@override
void initState() {
super.initState();
_sdk = SynheartWear(
config: SynheartWearConfig.withAdapters({DeviceAdapter.appleHealthKit}),
);
}
Future<void> _connect() async {
try {
final result = await _sdk.requestPermissions(
permissions: {
PermissionType.heartRate,
PermissionType.steps,
PermissionType.calories,
},
reason: 'This app needs access to your health data.',
);
if (result.values.any((s) => s == ConsentStatus.granted)) {
await _sdk.initialize();
final metrics = await _sdk.readMetrics();
setState(() {
_isConnected = true;
_latestMetrics = metrics;
});
}
} catch (e) {
print('Error: $e');
}
}
void _startStreaming() {
_hrSubscription = _sdk.streamHR(interval: Duration(seconds: 3))
.listen((metrics) {
setState(() => _latestMetrics = metrics);
});
}
@override
void dispose() {
_hrSubscription?.cancel();
_sdk.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Health Monitor')),
body: _isConnected
? Column(
children: [
if (_latestMetrics != null) ...[
Text('HR: ${_latestMetrics!.getMetric(MetricType.hr)} bpm'),
Text('Steps: ${_latestMetrics!.getMetric(MetricType.steps)}'),
],
ElevatedButton(
onPressed: _startStreaming,
child: Text('Start Streaming'),
),
],
)
: Center(
child: ElevatedButton(
onPressed: _connect,
child: Text('Connect to Health'),
),
),
);
}
}
Error Handling #
try {
final metrics = await synheart.readMetrics();
if (metrics.hasValidData) {
print('Data available');
}
} on PermissionDeniedError catch (e) {
print('Permission denied: $e');
} on DeviceUnavailableError catch (e) {
print('Device unavailable: $e');
} on SynheartWearError catch (e) {
print('SDK error: $e');
}
Architecture
┌─────────────────────────┐
│ synheart_wear SDK │
├─────────────────────────┤
│ Device Adapters Layer │
│ (Apple, Fitbit, etc.) │
├─────────────────────────┤
│ Normalization Engine │
│ (standard output schema)│
├─────────────────────────┤
│ Local Cache & Storage │
│ (encrypted, offline) │
└─────────────────────────┘
Roadmap
| Version | Goal | Status |
|---|---|---|
| v0.1 | Core SDK | ✅ Complete |
| v0.2 | Real-time streaming | ✅ Complete |
| v0.3 | Extended device support | 🔄 In Progress |
| v0.4 | SWIP integration | 📋 Planned |
| v1.0 | Public Release | 📋 Planned |
🤝 Contributing #
We welcome contributions! Please see our Contributing Guidelines or:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
👥 Authors #
- Israel Goytom - Initial work - @isrugeek
- Synheart AI Team - RFC Design & Architecture
Made with ❤️ by the Synheart AI Team
Technology with a heartbeat.