Overview
The Synheart Wear Flutter SDK provides a unified API for streaming biometric data from Apple Health, Health Connect, WHOOP, Garmin, Fitbit, Oura and BLE heart-rate straps in Flutter applications. Key Features:- Cross-platform support (iOS + Android)
- Real-time HR and HRV streaming
- Unified data schema across all devices
- Optional encrypted local cache (AES-256-GCM, key in the Keychain / Android secure storage)
- Consent-based permissions
Installation
Add to yourpubspec.yaml:
minSdk 28.
Platform Configuration
iOS Configuration
Add toios/Runner/Info.plist:
NSHealthUpdateUsageDescription
is not needed.
Android Configuration
Add toandroid/app/src/main/AndroidManifest.xml. The Flutter plugin’s own
manifest declares no health permissions, so your app must declare every type it
reads; a type that isn’t declared can’t be granted, and its reads come back
empty. (The Kotlin SDK is different: its manifest declares them for you.)
MainActivity must extend FlutterFragmentActivity (not FlutterActivity) for Android 14+.
Basic Usage
Recommended Pattern (Explicit Permission Control):initialize() request permissions itself:
initialize() validates that wearable data is available and not stale (>24 hours old). If no data is available or data is too old, it will throw a SynheartWearError with codes NO_WEARABLE_DATA or STALE_DATA.
Sleep and overnight history
HealthAdapter reads finished nights and their overnight physiology from
HealthKit or Health Connect. Results are keyed by the local day each night
ended on (midnight of the wake day).
- Pass
sleepNightstofetchOvernightPhysiology. Without it, HRV and HR spread come from the whole day, andovernightHrBpmstays null. - HRV is SDNN on iOS (
hrvSdnnMs) and RMSSD on Android (hrvRmssdMs); the other field is null. - A night crossing midnight stays whole; when a day has more than one sleep (a nap), the longest is kept.
- Days with no record are absent from the map, never zero.
- On Android, reads are throttled to one per 10 seconds to stay under Health
Connect’s quota; a throttled call returns an empty map. Pass
bypassThrottle: truefor a user-initiated refresh. - Android reads reach about 30 days unless the app holds the history
permission. Request it with
HealthAdapter.requestHistoryAuthorization(); see Reading past 30 days.
Reading past 30 days (Android)
Health Connect lets an app read only the 30 days before it was first granted access, for every record type, unless it also holdsREAD_HEALTH_DATA_HISTORY. Declare it in the manifest (see
Android Configuration), then ask for it; it is its
own prompt:
isHistoryAvailable() is false where the device’s Health Connect doesn’t
offer the permission; those devices stay at 30 days. On iOS there is no such
limit: the checks report authorized and make no platform call.
fetchActiveMinutes needs PermissionType.exercise, and on Android the
READ_EXERCISE permission. To push this history into the personal baselines,
see Health backfill.
Real-Time Streaming
Complete Example
Data Schema
Access metrics using theWearMetrics object:
Error Handling
NO_WEARABLE_DATA: No health data available from connected devicesSTALE_DATA: Latest data is older than 24 hoursPERMISSION_DENIED: User denied required permissions (thrown asPermissionDeniedError)
requestPermissions() accepts a reason: argument, but the SDK doesn’t show it
anywhere; explain the request in your own UI before calling it.
BLE Heart Rate Monitor
Connect directly to any standard Bluetooth LE heart rate monitor (WHOOP Broadcast, Polar, Wahoo, Garmin HRM straps) for real-time HR streaming. No cloud backend needed.Platform Setup
Android — Add toAndroidManifest.xml:
BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime yourself
(for example with permission_handler). BleHrmProvider.requestPermission() is
iOS-only; on Android it throws MissingPluginException.
iOS — Add to Info.plist:
Usage
enableMotion: true (and optionally motionSampleRateHz, default 50) to
connect() and listen to bleHrm.onMotion. getBondedHrDevices() lists
already-bonded straps without scanning.
HeartRateSample
Each sample from the stream contains:Error Handling
BLE calls throwBleHrmError, whose code is a BleHrmErrorCode. Errors on
the onHeartRate stream use the same type:
Notes
- WHOOP devices require “Broadcast Heart Rate” enabled in the WHOOP app settings
- RR intervals are available on some devices (e.g. Polar chest straps) but not all
- Reconnection is automatic. Android retries 5 times, 3 s apart; iOS waits up to 30 s for CoreBluetooth to reconnect. After that
disconnectedis reported - Only one BLE HRM device can be connected at a time
Cloud Providers
WhoopProvider
GarminProvider (Cloud)
Full Garmin Connect integration via OAuth PKCE. Supports 12 summary types through the Garmin Health API backfill endpoint, plus activities (fetchActivities()): dailies, epochs, sleeps, stressDetails, hrv, userMetrics, bodyComps, pulseox, respiration, healthSnapshot, bloodPressures, and skinTemp.
GarminHealth (Native RTS)
TheGarminHealth facade provides native Garmin device integration for scanning, pairing, and real-time streaming. All method signatures use generic SDK-owned types (ScannedDevice, PairedDevice, WearMetrics), never Garmin-specific types.
Important: The Garmin Health SDK Real-Time Streaming (RTS) capability requires a separate license from Garmin. This facade is available on demand for licensed integrations. The underlying Garmin Health SDK code is proprietary to Garmin and is not distributed as open source.
Platform-Specific Notes
Android
- HRV: Health Connect reports RMSSD only, as
hrv_rmssd;hrv_sdnnstays null - Distance: Uses
DISTANCE_DELTAtype readMetrics()calls are spaced at least 10 seconds apart to stay under Health Connect’s read quota. A call for the same data inside that gap gets the previous result; any other call waits out the gap, then reads.- Requires Health Connect app installed
- MainActivity must extend
FlutterFragmentActivity
iOS
- Requires iOS 16+ and the HealthKit capability enabled in Xcode
- HRV: HealthKit reports SDNN only, as
hrv_sdnn;hrv_rmssdstays null - No HealthKit background delivery: reads happen when your app calls the SDK
- Real-time reads look back 2 minutes and run at most every 10 s; calls in between return no new data
API Reference
SynheartWear
Main SDK class for interacting with wearable devices.
Constructor:
SynheartWearConfig
Configuration for the SDK.
Constructor:
WearMetrics
Biometric data container.
Methods:
Properties:
HealthAdapter
Static reads from HealthKit / Health Connect. See
Sleep and overnight history.
Resources
- Repository: synheart-wear-flutter
- pub.dev: synheart_wear
- API Docs: API Reference
- Issues: GitHub Issues