open_wearables_health_sdk 0.0.10
open_wearables_health_sdk: ^0.0.10 copied to clipboard
Flutter SDK for secure background health data synchronization from Apple HealthKit (iOS) and Samsung Health / Health Connect (Android) to the Open Wearables platform.
Open Wearables Health SDK (Flutter) #
A Flutter plugin for secure background health data synchronization from Apple HealthKit (iOS), Samsung Health, and Health Connect (Android) to your backend.
Part of Open Wearables - a self-hosted platform to unify wearable health data through one AI-ready API.
Features #
- Token Authentication - Sign in with accessToken + refreshToken, SDK handles refresh automatically
- Background Sync - Health data syncs even when app is in background
- Incremental Updates - Only syncs new data using anchored queries
- Secure Storage - Credentials stored in iOS Keychain / Android EncryptedSharedPreferences
- Wide Data Support - Steps, heart rate, workouts, sleep, and more
- Custom Host - Point the SDK at any compatible backend
- Multi-Provider (Android) - Samsung Health and Health Connect supported
Installation #
1. Add Dependency #
dependencies:
open_wearables_health_sdk: ^0.1.0
2. iOS Configuration #
Add to Info.plist:
<key>NSHealthShareUsageDescription</key>
<string>This app syncs your health data to your account.</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.openwearables.healthsdk.task.refresh</string>
<string>com.openwearables.healthsdk.task.process</string>
</array>
Enable HealthKit in Xcode: Target > Signing & Capabilities > + HealthKit.
3. Android Configuration #
Set minSdk to 29
In android/app/build.gradle.kts:
android {
defaultConfig {
minSdk = 29
}
}
Add Health Connect permission aliases to AndroidManifest.xml
In android/app/src/main/AndroidManifest.xml, inside the <application> tag:
<!-- Health Connect: permissions rationale (Android 14+) -->
<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>
<!-- Health Connect: permissions rationale (Android 12-13) -->
<activity-alias
android:name="ShowPermissionRationaleActivity"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity-alias>
Use FlutterFragmentActivity
In your MainActivity.kt, extend FlutterFragmentActivity instead of FlutterActivity:
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()
This is required for Health Connect permission dialogs to work.
Samsung Health
Samsung Health is supported out of the box - the Samsung Health Data SDK is bundled in the Android SDK repository. No extra download needed.
For testing on Samsung devices, enable Developer Mode in Samsung Health: Settings > About Samsung Health > tap version 10 times.
SDK Usage #
1. Configure (once at app start) #
await OpenWearablesHealthSdk.configure(
host: 'https://api.example.com',
);
// Session is automatically restored if user was previously signed in
if (OpenWearablesHealthSdk.isSignedIn) {
print('Welcome back, ${OpenWearablesHealthSdk.currentUser?.userId}!');
}
2. Sign In #
// Sign in with tokens (supports automatic token refresh)
try {
final user = await OpenWearablesHealthSdk.signIn(
userId: 'user-id',
accessToken: 'Bearer access-token',
refreshToken: 'refresh-token',
);
print('Connected: ${user.userId}');
} on SignInException catch (e) {
print('Failed: ${e.message}');
}
// Or with API key (simpler, no automatic token refresh):
final user = await OpenWearablesHealthSdk.signIn(
userId: 'your-user-id',
apiKey: 'your-api-key',
);
3. Select Provider (Android only) #
On Android, multiple health data providers may be available. Let the user choose:
import 'dart:io';
if (Platform.isAndroid) {
final providers = await OpenWearablesHealthSdk.getAvailableProviders();
// Show UI to let user pick a provider
// providers = [AvailableProvider(samsung, Samsung Health), AvailableProvider(google, Health Connect)]
await OpenWearablesHealthSdk.setProvider(AndroidHealthProvider.healthConnect);
// or: await OpenWearablesHealthSdk.setProvider(AndroidHealthProvider.samsungHealth);
}
4. Request Permissions #
final authorized = await OpenWearablesHealthSdk.requestAuthorization(
types: [
HealthDataType.steps,
HealthDataType.heartRate,
HealthDataType.sleep,
HealthDataType.workout,
],
);
5. Start Background Sync #
await OpenWearablesHealthSdk.startBackgroundSync();
6. Listen to Logs and Auth Errors (optional) #
OpenWearablesHealthSdk.logStream.listen((message) {
print('[SDK] $message');
});
OpenWearablesHealthSdk.authErrorStream.listen((error) {
print('Auth error: ${error['statusCode']} - ${error['message']}');
// Handle token expiration - redirect to login, etc.
});
7. Sign Out #
await OpenWearablesHealthSdk.signOut();
URL Structure #
When you provide a host (e.g. https://api.example.com), the SDK constructs endpoints automatically:
| Endpoint | URL |
|---|---|
| Health data sync | {host}/api/v1/sdk/users/{userId}/sync |
| Token refresh | {host}/api/v1/token/refresh |
You can also provide a customSyncUrl during configure() to override the sync endpoint.
Complete Example #
class HealthService {
final String host;
HealthService({required this.host});
Future<void> connect({
required String userId,
required String accessToken,
required String refreshToken,
}) async {
// 1. Configure SDK
await OpenWearablesHealthSdk.configure(host: host);
// 2. Check existing session
if (OpenWearablesHealthSdk.isSignedIn) {
if (!OpenWearablesHealthSdk.isSyncActive) {
await _startSync();
}
return;
}
// 3. Sign in
await OpenWearablesHealthSdk.signIn(
userId: userId,
accessToken: accessToken,
refreshToken: refreshToken,
);
// 4. Select provider on Android
if (Platform.isAndroid) {
await OpenWearablesHealthSdk.setProvider(
AndroidHealthProvider.healthConnect,
);
}
// 5. Start syncing
await _startSync();
}
Future<void> _startSync() async {
await OpenWearablesHealthSdk.requestAuthorization(
types: HealthDataType.values,
);
await OpenWearablesHealthSdk.startBackgroundSync();
}
Future<void> disconnect() async {
await OpenWearablesHealthSdk.stopBackgroundSync();
await OpenWearablesHealthSdk.signOut();
}
}
Supported Health Data Types #
Supported on all platforms #
| Category | Types |
|---|---|
| Activity | steps, distanceWalkingRunning, flightsClimbed |
| Energy | activeEnergy, basalEnergy |
| Heart | heartRate, restingHeartRate, heartRateVariabilitySDNN, vo2Max, oxygenSaturation |
| Respiratory | respiratoryRate |
| Body | bodyMass, height, bodyFatPercentage, leanBodyMass, bodyTemperature |
| Blood | bloodGlucose, bloodPressure, bloodPressureSystolic, bloodPressureDiastolic |
| Nutrition | dietaryWater |
| Sleep | sleep |
| Workouts | workout |
iOS only #
| Category | Types |
|---|---|
| Activity | distanceCycling, walkingSpeed, walkingStepLength, walkingAsymmetryPercentage, walkingDoubleSupportPercentage, sixMinuteWalkTestDistance |
| Body | bmi, waistCircumference (iOS 16+) |
| Glucose | insulinDelivery (iOS 16+) |
| Nutrition | dietaryEnergyConsumed, dietaryCarbohydrates, dietaryProtein, dietaryFatTotal |
| Sleep | mindfulSession |
| Reproductive | menstrualFlow, cervicalMucusQuality, ovulationTestResult, sexualActivity |
Android only #
| Category | Types |
|---|---|
| Activity | distanceCycling (Health Connect) |
Types not supported on the current platform are silently ignored during sync.
API Reference #
OpenWearablesHealthSdk #
| Method | Description |
|---|---|
configure({required host, customSyncUrl?}) |
Initialize SDK with host URL and restore session |
signIn({userId, accessToken?, refreshToken?, apiKey?}) |
Sign in with tokens or API key |
signOut() |
Sign out and clear all credentials |
updateTokens({accessToken, refreshToken?}) |
Update tokens without re-signing in |
requestAuthorization({types}) |
Request health data permissions |
startBackgroundSync() |
Enable background sync |
stopBackgroundSync() |
Disable background sync |
syncNow() |
Trigger immediate sync |
resetAnchors() |
Reset sync state (forces full re-export) |
getStoredCredentials() |
Get stored credentials for debugging |
getSyncStatus() |
Get current sync session status |
resumeSync() |
Manually resume interrupted sync |
clearSyncSession() |
Clear interrupted sync without resuming |
setProvider(AndroidHealthProvider) |
Set health data provider (Android only) |
getAvailableProviders() |
Get available providers on device (Android only) |
Properties #
| Property | Type | Description |
|---|---|---|
isConfigured |
bool |
SDK is configured |
isSignedIn |
bool |
User is signed in |
isSyncActive |
bool |
Background sync is active |
currentUser |
OpenWearablesHealthSdkUser? |
Current user info |
config |
OpenWearablesHealthSdkConfig? |
Current configuration |
status |
OpenWearablesHealthSdkStatus |
Current SDK status |
logStream |
Stream<String> |
SDK log messages |
authErrorStream |
Stream<Map> |
Auth error events |
Exceptions #
| Exception | When Thrown |
|---|---|
NotConfiguredException |
configure() was not called |
NotSignedInException |
No user signed in |
SignInException |
Sign-in failed |
Architecture #
┌──────────────────────────────────────────────────┐
│ Flutter App (Dart) │
│ OpenWearablesHealthSdk → MethodChannel │
└──────────────────┬───────────────────────────────┘
│
┌──────────────────▼───────────────────────────────┐
│ Flutter Plugin (thin wrapper) │
│ OpenWearablesHealthSdkPlugin.kt │
│ └── delegates to ↓ │
│ │
│ Open Wearables Android SDK (native library) │
│ OpenWearablesHealthSDK (facade) │
│ ├── SamsungHealthManager → Samsung Health │
│ ├── HealthConnectManager → Health Connect │
│ └── SyncManager → WorkManager background sync │
└───────────────────────────────────────────────────┘
On Android, the Flutter plugin is a thin wrapper around the Open Wearables Android SDK (com.openwearables.health:sdk), which can also be used independently in native Android apps.
License #
MIT License