health_connector 2.3.2 copy "health_connector: ^2.3.2" to clipboard
health_connector: ^2.3.2 copied to clipboard

The most comprehensive Flutter health plugin for seamless iOS HealthKit and Android Health Connect integration.

health_connector #

Pub Version Pub Points License Platform

The most complete Flutter health SDK β€” unified, type-safe access to 100+ health data types across iOS HealthKit and Android Health Connect.


πŸ“– Table of Contents #


πŸ“± Exploring SDK Capabilities using Health Connector Toolbox #

To explore the SDK's capabilities hands-on, you can use the Health Connector Toolbox app included in the repository.

Permission Request Read Data Write Data Delete Data Aggregate Data
[Permission Request] [Read Data] [Write Data] [Delete Data] [Aggregate Data]
[Permission Request] [Read Data] [Write Data] [Delete Data] [Aggregate Data]

ℹ️ Note: The toolbox is intended as a demonstration and internal testing tool only. It is not recommended as a reference for building production applications.

What the Toolbox Offers

  • Interactive demonstrations of core SDK capabilities
    • Permission management
    • Read, write, update, and delete operations
    • Data aggregation
    • Feature management
  • Visual representation of health records

Running the Toolbox

# Clone the repository
git clone https://github.com/fam-tung-lam/health_connector.git
cd health_connector

# Navigate to the toolbox app
cd examples/health_connector_toolbox

# Install dependencies
flutter pub get

# Run on your device
flutter run

πŸš€ Quick Start #

πŸ“‹ Requirements #

Platform Minimum Version
Android API 26+
iOS β‰₯15.0

πŸ“¦ Installation #

flutter pub add health_connector

Or add manually to pubspec.yaml:

dependencies:
  health_connector: [latest_version]

πŸ”§ Platform Setup #

πŸ€– Android Health Connect Setup
Step 1: Update AndroidManifest.xml

Add to android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <!-- Your existing configuration -->

        <!-- Health Connect intent filter for showing permissions rationale -->
        <activity-alias
            android:name="ViewPermissionUsageActivity"
            android:exported="true"
            android:targetActivity=".MainActivity"
            android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
            <intent-filter>
                <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
            </intent-filter>
        </activity-alias>
    </application>

    <!-- Declare Health Connect permissions for each data type you use -->

    <!-- Read permissions -->
    <uses-permission android:name="android.permission.health.READ_STEPS" />
    <uses-permission android:name="android.permission.health.READ_WEIGHT" />
    <uses-permission android:name="android.permission.health.READ_HEART_RATE" />
    <!-- Add more read permissions... -->
    
    <!-- Write permissions -->
    <uses-permission android:name="android.permission.health.WRITE_STEPS" />
    <uses-permission android:name="android.permission.health.WRITE_WEIGHT" />
    <uses-permission android:name="android.permission.health.WRITE_HEART_RATE" />
    <!-- Add more write permissions... -->

    <!-- Feature permissions -->
    <uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" />
    <uses-permission android:name="android.permission.health.READ_HEALTH_DATA_HISTORY" />
    <!-- Add more feature permissions... -->
</manifest>

❗ Important: You must declare a permission for each health data type and feature your app accesses. See the Health Connect data types list for all available permissions.

Step 2: Update MainActivity (Android 14+)

This SDK uses the modern registerForActivityResult API when requesting permissions from Health Connect. For this to work correctly, your app's MainActivity must extend FlutterFragmentActivity instead of FlutterActivity. This is required because registerForActivityResult is only available in ComponentActivity and its subclasses.

Update android/app/src/main/kotlin/.../MainActivity.kt:

package com.example.yourapp

import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterFragmentActivity() {
    // Your existing code
}
Step 3: Enable AndroidX

Health Connect is built on AndroidX libraries. android.useAndroidX=true enables AndroidX support, and android.enableJetifier=true automatically migrates third-party libraries to use AndroidX.

Update android/gradle.properties:

# Your existing configuration

android.enableJetifier=true
android.useAndroidX=true
Step 4: Update Gradle

Update android/app/build.gradle:

android {
    // Your existing configuration

    defaultConfig {
        // Your existing configuration

        minSdkVersion 26
    }
}
🍎 iOS HealthKit Setup
Step 1: Set Minimum iOS Version
  1. Open your project in Xcode (ios/Runner.xcworkspace)
  2. Select your app target
  3. Go to General tab
  4. Set Minimum Deployments to 15.0
Step 2: Enable HealthKit Capability
  1. Open your project in Xcode (ios/Runner.xcworkspace)
  2. Select your app target
  3. Go to Signing & Capabilities tab
  4. Click + Capability
  5. Add HealthKit
Step 3: Update Info.plist

Add to ios/Runner/Info.plist:

<dict>
    <!-- Existing keys -->

    <!-- Required: Describe why your app reads health data -->
    <key>NSHealthShareUsageDescription</key>
    <string>This app needs to read your health data to provide personalized insights.</string>

    <!-- Required: Describe why your app writes health data -->
    <key>NSHealthUpdateUsageDescription</key>
    <string>This app needs to save health data to track your progress.</string>
</dict>

⚠️ Warning: Vague or generic usage descriptions may result in App Store rejection. Be specific about what data you access and why.

⚑ Quick Demo #

import 'package:health_connector/health_connector.dart';

Future<void> quickStart() async {
  // 1. Check platform availability
  final status = await HealthConnector.getHealthPlatformStatus();
  if (status != HealthPlatformStatus.available) {
    print('Health platform not available: $status');
    return;
  }

  // 2. Create connector instance
  final connector = await HealthConnector.create(
    HealthConnectorConfig(isLoggerEnabled: true),
  );

  // 3. Request permissions for steps
  // **Note**: Feel free to use any other data type you want.
  final results = await connector.requestPermissions([
    HealthDataType.steps.readPermission,
    HealthDataType.steps.writePermission,
  ]);

  // 4. Check if all permissions were granted
  final arePermissionsGranted = results.every((r) => r.status != PermissionStatus.denied);

  if (!arePermissionsGranted) {
    print('Permissions not granted');
    return;
  }

  // 5. Write multiple step records
  final stepsRecords = [
    StepsRecord(
      startTime: DateTime.now().subtract(Duration(hours: 3)),
      endTime: DateTime.now().subtract(Duration(hours: 2)),
      count: Number(1500),
      metadata: Metadata.automaticallyRecorded(
        dataOrigin: DataOrigin('com.example'),
        device: Device.fromType(DeviceType.phone),
      ),
    ),
    StepsRecord(
      startTime: DateTime.now().subtract(Duration(hours: 2)),
      endTime: DateTime.now().subtract(Duration(hours: 1)),
      count: Number(2000),
      metadata: Metadata.automaticallyRecorded(
        dataOrigin: DataOrigin('com.example'),
        device: Device.fromType(DeviceType.phone),
      ),
    ),
    StepsRecord(
      startTime: DateTime.now().subtract(Duration(hours: 1)),
      endTime: DateTime.now(),
      count: Number(1800),
      metadata: Metadata.automaticallyRecorded(
        dataOrigin: DataOrigin('com.example'),
        device: Device.fromType(DeviceType.phone),
      ),
    ),
  ];
  final recordIds = await connector.writeRecords(stepsRecords);
  print('Wrote ${recordIds.length} records');


  // 6. Read today's step records
  final response = await connector.readRecords(
    HealthDataType.steps.readInTimeRange(
      startTime: DateTime.now().subtract(Duration(days: 1)),
      endTime: DateTime.now(),
    ),
  );

  for (final record in response.records) {
    print('Record: ${record.count.value} (${record.startTime} - ${record.endTime})');
  }
}

πŸ“˜ Developer Guide #

πŸ” Permission Management #

Request Permissions

⚠️ iOS Privacy Limitation: HealthKit always returns PermissionStatus.unknown for read permissions to protect user privacy. This is Apple's intentional design, not a limitation of this SDK.

final permissions = [
  // Data permissions
  HealthDataType.steps.readPermission,
  HealthDataType.steps.writePermission,
  HealthDataType.weight.readPermission,
  HealthDataType.weight.writePermission,
  
  // Feature permissions
  HealthPlatformFeature.readHealthDataInBackground.permission,
];

final results = await connector.requestPermissions(permissions);

for (final result in results) {
  print('${result.permission}: ${result.status}');
}
iOS Read Permission Workaround

While the SDK respects Apple's privacy guidelines by returning unknown for read permissions, developers who need to determine actual read permission status can use the following workaround:

Workaround Strategy: Attempt to read a small amount of data. If the read fails with NotAuthorizedException, the permission is denied. If it succeeds (even with empty results), the permission is granted.

Future<bool> checkIOSReadPermission(HealthDataType dataType) async {
  try {
    // Attempt to read a minimal amount of data
    final request = dataType.readInTimeRange(
      startTime: DateTime.now().subtract(Duration(days: 1)),
      endTime: DateTime.now(),
      pageSize: 1, // Minimal data fetch
    );
    
    await connector.readRecords(request);
    
    // If read succeeds, permission is granted
    return true;
  } on NotAuthorizedException {
    // Permission was denied
    return false;
  }
}

⚠️ Important Considerations:

  • This workaround is not officially recommended by Apple
  • The SDK intentionally does not implement this as default behavior to ensure developers are aware of the privacy implications

Check Individual Permission Status

final status = await connector.getPermissionStatus(
  HealthDataType.steps.readPermission,
);

switch (status) {
  case PermissionStatus.granted:
    print('Permission granted');
  case PermissionStatus.denied:
    print('Permission denied');
  case PermissionStatus.unknown:
    print('Cannot determine (iOS read permission)');
}

Get All Granted Permissions (Android Health Connect Only)

iOS Privacy Note: This API is not available on iOS. HealthKit does not provide a way to query all granted permissions to protect user privacy. Apps cannot enumerate what health data access they have been granted.

try {
  final grantedPermissions = await connector.getGrantedPermissions();
  for (final permission in grantedPermissions) {
    if (permission is HealthDataPermission) {
      print('${permission.dataType} (${permission.accessType})');
    }
  }
} on UnsupportedOperationException {
  print('Only available on Android');
}

Revoke All Permissions (Android Health Connect Only)

iOS Privacy Note: This API is not available on iOS. HealthKit requires users to manually revoke permissions through the iOS Settings app. This ensures users have full control and visibility over their health data permissions.

try {
  await connector.revokeAllPermissions();
} on UnsupportedOperationException {
  print('Only available on Android');
}

πŸ“– Reading Health Data #

Platform Note - Historical Data Access

Android Health Connect: By default, Health Connect only provides access to the last 30 days of historical health data. To read data older than 30 days, the HealthPlatformFeature.readHealthDataHistory feature must be available and its permission must be granted.

iOS HealthKit: HealthKit has no default limitation on historical data access. Apps can read health data from any time period, subject only to user permission.

Read by ID

final recordId = HealthRecordId('existing-record-id');
final request = HealthDataType.steps.readRecord(recordId);
final record = await connector.readRecord(request);

if (record != null) {
  print('Steps: ${record.count.value}');
} else {
  print('Record not found');
}

Read Multiple Records

final request = HealthDataType.steps.readInTimeRange(
  startTime: DateTime.now().subtract(Duration(days: 7)),
  endTime: DateTime.now(),
  pageSize: 100,
);

final response = await connector.readRecords(request);

for (final record in response.records) {
  print('Steps: ${record.count.value} (${record.startTime} - ${record.endTime})');
}

Pagination

var request = HealthDataType.steps.readInTimeRange(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
  pageSize: 100,
);

final allRecords = <StepsRecord>[];

while (true) {
  final response = await connector.readRecords(request);
  allRecords.addAll(response.records.cast<StepsRecord>());

  if (response.nextPageRequest == null) break;
  request = response.nextPageRequest!;
}

print('Total records: ${allRecords.length}');

πŸ’Ύ Writing Health Data #

Write Single Record

final stepRecord = StepsRecord(
  id: HealthRecordId.none, // Must be .none for new records
  startTime: DateTime.now().subtract(Duration(hours: 1)),
  endTime: DateTime.now(),
  count: Number(5000),
  metadata: Metadata.automaticallyRecorded(
    device: Device.fromType(DeviceType.phone),
  ),
);

final recordId = await connector.writeRecord(stepRecord);
print('Record ID: $recordId');

Atomic Write Multiple Records

All records succeed or all fail together:

final records = [
  StepsRecord(
    id: HealthRecordId.none,
    startTime: DateTime.now().subtract(Duration(hours: 2)),
    endTime: DateTime.now().subtract(Duration(hours: 1)),
    count: Number(3000),
    metadata: Metadata.automaticallyRecorded(
      device: Device.fromType(DeviceType.phone),
    ),
  ),
  StepsRecord(
    id: HealthRecordId.none,
    startTime: DateTime.now().subtract(Duration(hours: 1)),
    endTime: DateTime.now(),
    count: Number(2000),
    metadata: Metadata.automaticallyRecorded(
      device: Device.fromType(DeviceType.phone),
    ),
  ),
];

final recordIds = await connector.writeRecords(records);
print('Wrote ${recordIds.length} records');

✍️ Updating Health Records #

iOS Privacy Note: HealthKit does not provide an update API because it uses an immutable data model. Once a health record is written to HealthKit, it cannot be modifiedβ€”only deleted.

Update Record (Android Health Connect Only)

final recordId = HealthRecordId('existing-record-id');
final request = HealthDataType.steps.readRecord(recordId);
final existingRecord = await connector.readRecord(request);

if (existingRecord != null) {
  final updatedRecord = existingRecord.copyWith(
    count: Number(existingRecord.count.value + 500),
  );

  await connector.updateRecord(updatedRecord);
  print('Record updated');
}

Atomic Update Multiple Records (Android Health Connect Only)

Update multiple records atomicallyβ€”all succeed or all fail together:

// Read existing records
final readRequest = HealthDataType.steps.readInTimeRange(
  startTime: DateTime.now().subtract(Duration(days: 7)),
  endTime: DateTime.now(),
);
final response = await connector.readRecords(readRequest);

// Update all records by adding 100 steps to each
final updatedRecords = response.records.map((record) {
  return record.copyWith(
    count: Number(record.count.value + 100),
  );
}).toList();

// Batch update all records
await connector.updateRecords(updatedRecords);
print('Updated ${updatedRecords.length} records');

iOS HealthKit Update Workaround (Delete + Write)

// 1. Delete existing record
await connector.deleteRecords(
  HealthDataType.steps.deleteByIds([existingRecord.id]),
);

// 2. Write new record with updated values
final newRecord = StepsRecord(
  id: HealthRecordId.none,
  startTime: existingRecord.startTime,
  endTime: existingRecord.endTime,
  count: Number(newValue),
  metadata: existingRecord.metadata,
);

final newId = await connector.writeRecord(newRecord);
// Note: newId will be different from the original ID

πŸ—‘οΈ Deleting Health Records #

Atomic Delete by IDs

await connector.deleteRecords(
  HealthDataType.steps.deleteByIds([
    HealthRecordId('id-1'),
    HealthRecordId('id-2'),
  ]),
);

Atomic Delete by Time Range

await connector.deleteRecords(
  HealthDataType.steps.deleteInTimeRange(
    startTime: DateTime.now().subtract(Duration(days: 7)),
    endTime: DateTime.now(),
  ),
);

Important: Apps can only delete records they created. Attempting to delete records from other apps will lead to NotAuthorizedException.

βž• Aggregating Health Data #

Sum Aggregation

Get the total value over a period, such as total steps for a day:

final sumRequest = HealthDataType.steps.aggregateSum(
  startTime: DateTime.now().subtract(Duration(days: 1)),
  endTime: DateTime.now(),
);

final sumResponse = await connector.aggregate(sumRequest);
print('Total steps: ${sumResponse.value.value}');

Average Aggregation

Get the average value over 30 days:

final avgRequest = HealthDataType.weight.aggregateAvg(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);

final avgResponse = await connector.aggregate(avgRequest);
print('Average weight: ${avgResponse.value.inKilograms} kg');

Minimum Aggregation

Get the minimum recorded value over a period:

final minRequest = HealthDataType.weight.aggregateMin(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);

final minResponse = await connector.aggregate(minRequest);
print('Min weight: ${minResponse.value.inKilograms} kg');

Maximum Aggregation

Get the maximum recorded value over a period:

final maxRequest = HealthDataType.weight.aggregateMax(
  startTime: DateTime.now().subtract(Duration(days: 30)),
  endTime: DateTime.now(),
);

final maxResponse = await connector.aggregate(maxRequest);
print('Max weight: ${maxResponse.value.inKilograms} kg');

βš™οΈ Feature Management #

Platform features have different availability characteristics across platforms.

Platform Differences

  • iOS HealthKit:
    • All features are available and granted by default
    • When checking feature status with getFeatureStatus(), the SDK always returns HealthPlatformFeatureStatus.available
    • When requesting feature permissions with requestPermissions(), the SDK always returns PermissionStatus.granted
  • Android Health Connect:
    • Feature availability depends on Android version and Health Connect SDK version
    • Some features require specific minimum versions, f.e. background health data reading requires Health Connect SDK v1.1.0-alpha04

Checking Feature Availability

// Check if background reading is available
final status = await connector.getFeatureStatus(
  HealthPlatformFeature.readHealthDataInBackground,
);

if (status == HealthPlatformFeatureStatus.available) {
  // Feature is supported - safe to request permission
  await connector.requestPermissions([
    HealthPlatformFeature.readHealthDataInBackground.permission,
  ]);
} else {
  // Feature not available on this device/version
  print('Background reading not available');
  // Implement fallback or disable feature in UI
}

Feature Permission Status

When checking the status of a feature permission:

final permissionStatus = await connector.getPermissionStatus(
  HealthPlatformFeature.readHealthDataInBackground.permission,
);

// On iOS: Always returns PermissionStatus.granted
// On Android: Returns actual status (granted/denied)

⚠️ Error Handling #

The SDK provides two approaches for handling errors.

Approach 1: Catching Specific Exceptions

Use Dart's type-based exception handling to catch specific error types.

try {
  await connector.requestPermissions([...]);
  await connector.writeRecord(record);
} on NotAuthorizedException catch (e) {
  // User denied or revoked permissions
  // Recovery: Explain why permission is needed, guide to settings
  print('Permission denied: ${e.message}');
} on InvalidConfigurationException catch (e) {
  // Missing AndroidManifest.xml or Info.plist configuration
  // Recovery: Fix app configuration (development-time error)
  print('Configuration error: ${e.message}');
} on UnsupportedOperationException catch (e) {
  // Platform doesn't support this operation
  // Recovery: Check platform before calling, use alternative approach
  print('Not supported: ${e.message}');
} on InvalidArgumentException catch (e) {
  // Invalid input (e.g., startTime > endTime, negative values)
  // Recovery: Validate inputs before calling
  print('Invalid argument: ${e.message}');
} on HealthPlatformUnavailableException catch (e) {
  // Device doesn't support health API
  // Recovery: Disable health features for this device
  print('Health unavailable: ${e.message}');
} on HealthPlatformNotInstalledOrUpdateRequiredException catch (e) {
  // Health Connect needs installation/update (Android Health Connect Only)
  // Recovery: Prompt user to install/update Health Connect
  print('Health Connect needs update: ${e.message}');
} on RemoteErrorException catch (e) {
  // Transient I/O or communication error
  // Recovery: Retry with exponential backoff
  print('Remote error: ${e.message}');
} on HealthConnectorException catch (e) {
  // Generic fallback for unexpected errors
  print('Unknown error [${e.code}]: ${e.message}');
}

Approach 2: Handling by Error Code

Catch the base HealthConnectorException and switch on HealthConnectorErrorCode.

try {
  await connector.requestPermissions([...]);
  await connector.writeRecord(record);
} on HealthConnectorException catch (e) {
  switch (e.code) {
    case HealthConnectorErrorCode.notAuthorized:
      print('Permission denied: ${e.message}');
    case HealthConnectorErrorCode.invalidConfiguration:
      print('Configuration error: ${e.message}');
    case HealthConnectorErrorCode.unsupportedOperation:
      print('Not supported: ${e.message}');
    case HealthConnectorErrorCode.invalidArgument:
      print('Invalid argument: ${e.message}');
    case HealthConnectorErrorCode.healthPlatformUnavailable:
      print('Health unavailable: ${e.message}');
    case HealthConnectorErrorCode.healthPlatformNotInstalledOrUpdateRequired:
      print('Health Connect needs update: ${e.message}');
    case HealthConnectorErrorCode.remoteError:
      print('Remote error: ${e.message}');
    case HealthConnectorErrorCode.unknown:
      print('Unknown error [${e.code}]: ${e.message}');
  }
}

Exception Quick Reference

Exception Cause Recovery
NotAuthorizedException Permission denied/revoked Guide user to settings
InvalidConfigurationException Missing manifest entries Fix configuration
UnsupportedOperationException API not available Check platform first
InvalidArgumentException Invalid input values Validate inputs
HealthPlatformUnavailableException Device unsupported Disable health features
HealthPlatformNotInstalledOrUpdateRequiredException Health Connect app missing Prompt installation

πŸ”§ Troubleshooting #

Common Issues

Issue Platform Solution
HealthPlatformUnavailableException iOS HealthKit API Add HealthKit capability in Xcode β†’ Signing & Capabilities
HealthPlatformUnavailableException Android Health Connect API Device doesn't support Health Connect (requires Android 8.0+)
HealthPlatformNotInstalledOrUpdateRequiredException Android Health Connect API Prompt user to install Health Connect
InvalidConfigurationException Android Health Connect API Add required permissions to AndroidManifest.xml
InvalidConfigurationException iOS HealthKit API Add NSHealthShareUsageDescription and NSHealthUpdateUsageDescription to Info.plist
Read permissions return unknown iOS HealthKit API Normal behaviorβ€”iOS doesn't expose read permission status for privacy
Can't delete/update records Both Apps can only modify records they created

Debug Logging

Enable detailed logs to troubleshoot issues:

final connector = await HealthConnector.create(
  HealthConnectorConfig(isLoggerEnabled: true),
);

❓ FAQ #

Why do iOS read permissions always return unknown?

Apple intentionally hides read permission status to protect user privacy. This prevents apps from inferring whether a user has any health data by checking if read permission was denied.

How do I handle Health Connect not being installed?

final status = await HealthConnector.getHealthPlatformStatus();
if (status == HealthPlatformStatus.installationOrUpdateRequired) {
  // Show dialog prompting user to install Health Connect
  // Then launch the app store page:
  await HealthConnector.launchHealthAppPageInAppStore();
}

What's the difference between heartRateSeriesRecord and heartRateMeasurementRecord?

  • Android Health Connect: Uses heartRateSeriesRecord β€” a single record with multiple samples over a time interval
  • iOS HealthKit: Uses heartRateMeasurementRecord β€” each measurement is a separate record with its own ID

Can I read health data from other apps?

Yes, with user permission. When granted read access, you can read health data from all sources ( other apps, devices, manual entries).

Can I delete health data from other apps?

No. Apps can only delete records they created. This is a platform security restriction.

🎯 Real-World Use Cases #

Fitness Tracker

Track daily activity with steps, calories, and distance:

Future<Map<String, double>> getDailyActivitySummary(
  HealthConnector connector,
  DateTime date,
) async {
  final startOfDay = DateTime(date.year, date.month, date.day);
  final endOfDay = startOfDay.add(Duration(days: 1));

  final steps = await connector.aggregate(
    HealthDataType.steps.aggregateSum(
      startTime: startOfDay,
      endTime: endOfDay,
    ),
  );

  final calories = await connector.aggregate(
    HealthDataType.activeCaloriesBurned.aggregateSum(
      startTime: startOfDay,
      endTime: endOfDay,
    ),
  );

  return {
    'steps': steps.value.value,
    'calories': calories.value.inKilocalories,
  };
}

Health Dashboard

Display vital signs with recent measurements:

Future<void> displayVitals(HealthConnector connector) async {
  final now = DateTime.now();
  final weekAgo = now.subtract(Duration(days: 7));

  // Get latest weight
  final weightResponse = await connector.readRecords(
    HealthDataType.weight.readInTimeRange(
      startTime: weekAgo,
      endTime: now,
      pageSize: 1,
    ),
  );

  if (weightResponse.records.isNotEmpty) {
    final latestWeight = weightResponse.records.first as WeightRecord;
    print('Latest weight: ${latestWeight.weight.inKilograms} kg');
  }

  // Get heart rate average
  final heartRateAvg = await connector.aggregate(
    HealthDataType.restingHeartRate.aggregateAvg(
      startTime: weekAgo,
      endTime: now,
    ),
  );
  print('Avg resting HR: ${heartRateAvg.value.inBeatsPerMinute} bpm');
}

Nutrition Logger

Log meals with macronutrients:

Future<void> logMeal({
  required HealthConnector connector,
  required String mealName,
  required double calories,
  required double proteinGrams,
  required double carbsGrams,
  required double fatGrams,
}) async {
  final now = DateTime.now();

  final nutritionRecord = NutritionRecord(
    id: HealthRecordId.none,
    startTime: now.subtract(Duration(minutes: 30)),
    endTime: now,
    mealType: MealType.lunch,
    name: mealName,
    energy: Energy.kilocalories(calories),
    protein: Mass.grams(proteinGrams),
    totalCarbohydrate: Mass.grams(carbsGrams),
    totalFat: Mass.grams(fatGrams),
    metadata: Metadata.manual(),
  );

  await connector.writeRecord(nutritionRecord);
  print('Meal logged: $mealName');
}

πŸ“š References #

πŸ“‹ Supported Health Data Types #

πŸƒ Activity

General Activity
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Steps Number of steps taken HealthDataType.steps Sum StepsRecord HKQuantityTypeIdentifier.stepCount
Active Calories Burned Energy burned through active movement HealthDataType.activeCaloriesBurned Sum ActiveCaloriesBurnedRecord HKQuantityTypeIdentifier.activeEnergyBurned
Floors Climbed Number of floors (flights of stairs) climbed HealthDataType.floorsClimbed Sum FloorsClimbedRecord HKQuantityTypeIdentifier.flightsClimbed
Sexual Activity Sexual activity tracking HealthDataType.sexualActivity - SexualActivityRecord HKCategoryTypeIdentifier.sexualActivity
Wheelchair Pushes Number of wheelchair pushes HealthDataType.wheelchairPushes Sum WheelchairPushesRecord HKQuantityTypeIdentifier.pushCount
Cycling Cadence Cycling pedaling cadence HealthDataType.cyclingPedalingCadence Avg, Min, Max CyclingPedalingCadenceRecord HKQuantityTypeIdentifier.cyclingCadence
Total Calories Burned Total energy burned (active + basal) HealthDataType.totalCaloriesBurned Sum TotalCaloriesBurnedRecord -
Basal Energy Burned Energy burned by basal metabolism HealthDataType.basalEnergyBurned Sum - HKQuantityTypeIdentifier.basalEnergyBurned
Distance Types
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Distance (generic) Generic distance traveled HealthDataType.distance Sum DistanceRecord -
Walking/Running Distance Distance covered by walking or running HealthDataType.walkingRunningDistance Sum - HKQuantityTypeIdentifier.distanceWalkingRunning
Cycling Distance Distance covered by cycling HealthDataType.cyclingDistance Sum - HKQuantityTypeIdentifier.distanceCycling
Swimming Distance Distance covered by swimming HealthDataType.swimmingDistance Sum - HKQuantityTypeIdentifier.distanceSwimming
Wheelchair Distance Distance covered using a wheelchair HealthDataType.wheelchairDistance Sum - HKQuantityTypeIdentifier.distanceWheelchair
Downhill Snow Sports Distance Distance covered during downhill snow sports HealthDataType.downhillSnowSportsDistance Sum - HKQuantityTypeIdentifier.distanceDownhillSnowSports
Cross Country Skiing Distance Distance covered during cross country skiing HealthDataType.crossCountrySkiingDistance Sum - HKQuantityTypeIdentifier.distanceCrossCountrySkiing
Paddle Sports Distance Distance covered during paddle sports HealthDataType.paddleSportsDistance Sum - HKQuantityTypeIdentifier.distancePaddleSports
Rowing Distance Distance covered during rowing HealthDataType.rowingDistance Sum - HKQuantityTypeIdentifier.distanceRowing
Skating Sports Distance Distance covered during skating sports HealthDataType.skatingSportsDistance Sum - HKQuantityTypeIdentifier.distanceSkatingSports
Six Minute Walk Test Distance Distance covered during 6-minute walk test HealthDataType.sixMinuteWalkTestDistance Sum - HKQuantityTypeIdentifier.sixMinuteWalkTestDistance
Speed Types
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Speed Series Speed measurements over time HealthDataType.speedSeries - SpeedRecord -
Walking Speed Walking speed measurement HealthDataType.walkingSpeed - - HKQuantityTypeIdentifier.walkingSpeed
Running Speed Running speed measurement HealthDataType.runningSpeed - - HKQuantityTypeIdentifier.runningSpeed
Stair Ascent Speed Speed while climbing stairs HealthDataType.stairAscentSpeed - - HKQuantityTypeIdentifier.stairAscentSpeed
Stair Descent Speed Speed while descending stairs HealthDataType.stairDescentSpeed - - HKQuantityTypeIdentifier.stairDescentSpeed
Power Types
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Power Series Power measurements over time HealthDataType.powerSeries Avg, Min, Max PowerRecord -
Cycling Power Power output during cycling HealthDataType.cyclingPower Avg, Min, Max - HKQuantityTypeIdentifier.cyclingPower
Exercise Sessions
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Exercise Session Complete workout session with exercise type and stats HealthDataType.exerciseSession Duration ExerciseSessionRecord HKWorkout
Exercise Types
Exercise Type Android Health Connect iOS HealthKit
ExerciseType.other βœ… βœ…
ExerciseType.running βœ… βœ…
ExerciseType.runningTreadmill βœ… ❌
ExerciseType.walking βœ… βœ…
ExerciseType.cycling βœ… βœ…
ExerciseType.cyclingStationary βœ… ❌
ExerciseType.hiking βœ… βœ…
ExerciseType.handCycling ❌ βœ…
ExerciseType.trackAndField ❌ βœ…
ExerciseType.swimming ❌ βœ…
ExerciseType.swimmingOpenWater βœ… ❌
ExerciseType.swimmingPool βœ… ❌
ExerciseType.surfing βœ… βœ…
ExerciseType.waterPolo βœ… βœ…
ExerciseType.rowing βœ… βœ…
ExerciseType.sailing βœ… βœ…
ExerciseType.paddling βœ… βœ…
ExerciseType.diving βœ… βœ…
ExerciseType.waterFitness ❌ βœ…
ExerciseType.waterSports ❌ βœ…
ExerciseType.strengthTraining βœ… βœ…
ExerciseType.weightlifting βœ… ❌
ExerciseType.calisthenics βœ… ❌
ExerciseType.basketball βœ… βœ…
ExerciseType.soccer βœ… βœ…
ExerciseType.americanFootball βœ… βœ…
ExerciseType.frisbeeDisc βœ… βœ…
ExerciseType.australianFootball βœ… βœ…
ExerciseType.baseball βœ… βœ…
ExerciseType.softball βœ… βœ…
ExerciseType.volleyball βœ… βœ…
ExerciseType.rugby βœ… βœ…
ExerciseType.cricket βœ… βœ…
ExerciseType.handball βœ… βœ…
ExerciseType.iceHockey βœ… ❌
ExerciseType.rollerHockey βœ… ❌
ExerciseType.hockey ❌ βœ…
ExerciseType.lacrosse ❌ βœ…
ExerciseType.discSports ❌ βœ…
ExerciseType.tennis βœ… βœ…
ExerciseType.tableTennis βœ… βœ…
ExerciseType.badminton βœ… βœ…
ExerciseType.squash βœ… βœ…
ExerciseType.racquetball βœ… βœ…
ExerciseType.pickleball ❌ βœ…
ExerciseType.skiing βœ… ❌
ExerciseType.snowboarding βœ… βœ…
ExerciseType.snowshoeing βœ… ❌
ExerciseType.skating βœ… βœ…
ExerciseType.crossCountrySkiing ❌ βœ…
ExerciseType.curling ❌ βœ…
ExerciseType.downhillSkiing ❌ βœ…
ExerciseType.snowSports ❌ βœ…
ExerciseType.boxing βœ… βœ…
ExerciseType.kickboxing ❌ βœ…
ExerciseType.martialArts βœ… βœ…
ExerciseType.wrestling ❌ βœ…
ExerciseType.fencing βœ… βœ…
ExerciseType.taiChi ❌ βœ…
ExerciseType.dancing βœ… ❌
ExerciseType.gymnastics βœ… βœ…
ExerciseType.barre ❌ βœ…
ExerciseType.cardioDance ❌ βœ…
ExerciseType.socialDance ❌ βœ…
ExerciseType.yoga βœ… βœ…
ExerciseType.pilates βœ… βœ…
ExerciseType.highIntensityIntervalTraining βœ… βœ…
ExerciseType.elliptical βœ… βœ…
ExerciseType.exerciseClass βœ… ❌
ExerciseType.bootCamp βœ… ❌
ExerciseType.guidedBreathing βœ… ❌
ExerciseType.stairClimbing βœ… βœ…
ExerciseType.crossTraining ❌ βœ…
ExerciseType.jumpRope ❌ βœ…
ExerciseType.fitnessGaming ❌ βœ…
ExerciseType.mixedCardio ❌ βœ…
ExerciseType.cooldown ❌ βœ…
ExerciseType.flexibility βœ… βœ…
ExerciseType.mindAndBody ❌ βœ…
ExerciseType.preparationAndRecovery ❌ βœ…
ExerciseType.stepTraining ❌ βœ…
ExerciseType.coreTraining ❌ βœ…
ExerciseType.golf βœ… βœ…
ExerciseType.archery ❌ βœ…
ExerciseType.bowling ❌ βœ…
ExerciseType.paragliding βœ… ❌
ExerciseType.climbing βœ… βœ…
ExerciseType.equestrianSports ❌ βœ…
ExerciseType.fishing ❌ βœ…
ExerciseType.hunting ❌ βœ…
ExerciseType.play ❌ βœ…
ExerciseType.wheelchair βœ… ❌
ExerciseType.wheelchairWalkPace ❌ βœ…
ExerciseType.wheelchairRunPace ❌ βœ…
ExerciseType.transition ❌ βœ…
ExerciseType.swimBikeRun ❌ βœ…

πŸ“ Body Measurements

Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Weight Body weight measurement HealthDataType.weight Avg, Min, Max WeightRecord HKQuantityTypeIdentifier.bodyMass
Height Body height measurement HealthDataType.height Avg, Min, Max HeightRecord HKQuantityTypeIdentifier.height
Body Fat Percentage Percentage of body fat HealthDataType.bodyFatPercentage Avg, Min, Max BodyFatRecord HKQuantityTypeIdentifier.bodyFatPercentage
Lean Body Mass Mass of body excluding fat HealthDataType.leanBodyMass Avg, Min, Max LeanBodyMassRecord HKQuantityTypeIdentifier.leanBodyMass
Body Temperature Core body temperature HealthDataType.bodyTemperature Avg, Min, Max BodyTemperatureRecord HKQuantityTypeIdentifier.bodyTemperature
Body Water Mass Mass of body water HealthDataType.bodyWaterMass Avg, Min, Max BodyWaterMassRecord -
Bone Mass Mass of bone mineral HealthDataType.boneMass Avg, Min, Max BoneMassRecord -
Body Mass Index Body Mass Index (BMI) HealthDataType.bodyMassIndex Avg, Min, Max - HKQuantityTypeIdentifier.bodyMassIndex
Waist Circumference Waist circumference measurement HealthDataType.waistCircumference Avg, Min, Max - HKQuantityTypeIdentifier.waistCircumference

❀️ Vitals

Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Heart Rate Series Heart rate measurements over time HealthDataType.heartRateSeriesRecord Avg, Min, Max HeartRateRecord -
Heart Rate Measurement Single heart rate measurement HealthDataType.heartRateMeasurementRecord Avg, Min, Max - HKQuantityTypeIdentifier.heartRate
Resting Heart Rate Heart rate while at rest HealthDataType.restingHeartRate Avg, Min, Max RestingHeartRateRecord HKQuantityTypeIdentifier.restingHeartRate
Blood Pressure Systolic and diastolic blood pressure HealthDataType.bloodPressure Avg, Min, Max BloodPressureRecord HKCorrelationTypeIdentifier.bloodPressure
Systolic Blood Pressure Upper blood pressure value HealthDataType.systolicBloodPressure Avg, Min, Max - HKQuantityTypeIdentifier.bloodPressureSystolic
Diastolic Blood Pressure Lower blood pressure value HealthDataType.diastolicBloodPressure Avg, Min, Max - HKQuantityTypeIdentifier.bloodPressureDiastolic
Oxygen Saturation Blood oxygen saturation percentage HealthDataType.oxygenSaturation Avg, Min, Max OxygenSaturationRecord HKQuantityTypeIdentifier.oxygenSaturation
Respiratory Rate Breathing rate (breaths per minute) HealthDataType.respiratoryRate Avg, Min, Max RespiratoryRateRecord HKQuantityTypeIdentifier.respiratoryRate
VOβ‚‚ Max Maximum oxygen consumption HealthDataType.vo2Max Avg, Min, Max Vo2MaxRecord HKQuantityTypeIdentifier.vo2Max
Blood Glucose Blood glucose concentration HealthDataType.bloodGlucose Avg, Min, Max BloodGlucoseRecord HKQuantityTypeIdentifier.bloodGlucose
HRV RMSSD Heart Rate Variability (RMSSD) HealthDataType.heartRateVariabilityRmssd - HeartRateVariabilityRmssdRecord -
HRV SDNN Heart Rate Variability (SDNN) HealthDataType.heartRateVariabilitySdnn - - HKQuantityTypeIdentifier.heartRateVariabilitySDNN

😴 Sleep

Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Sleep Session Complete sleep session with sleep stages HealthDataType.sleepSession - SleepSessionRecord -
Sleep Stage Record Individual sleep stage measurement HealthDataType.sleepStageRecord - - HKCategoryTypeIdentifier.sleepAnalysis

πŸ˜‹ Nutrition

Core & Hydration
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Nutrition (composite) Complete nutrition record with macros and micronutrients HealthDataType.nutrition - NutritionRecord HKCorrelationType.food
Energy Total energy intake from food HealthDataType.energyNutrient (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.energy field) HKQuantityTypeIdentifier.dietaryEnergyConsumed
Hydration/Water Water and fluid intake HealthDataType.hydration Sum HydrationRecord HKQuantityTypeIdentifier.dietaryWater
Macronutrients
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Protein Protein intake HealthDataType.protein (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.protein) HKQuantityTypeIdentifier.dietaryProtein
Total Carbohydrate Total carbs intake HealthDataType.totalCarbohydrate (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.carbs) HKQuantityType Identifier.dietaryCarbohydrates
Total Fat Total fat intake HealthDataType.totalFat (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.totalFat) HKQuantityTypeIdentifier.dietaryFatTotal
Caffeine Caffeine intake HealthDataType.caffeine (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.caffeine) HKQuantityTypeIdentifier.dietaryCaffeine
Fats
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Saturated Fat Saturated fat intake HealthDataType.saturatedFat (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.saturatedFat) HKQuantityTypeIdentifier.dietaryFatSaturated
Monounsaturated Fat Monounsaturated fat intake HealthDataType.monounsaturatedFat (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.monounsaturatedFat) HKQuantityTypeIdentifier.dietaryFatMonounsaturated
Polyunsaturated Fat Polyunsaturated fat intake HealthDataType.polyunsaturatedFat (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.polyunsaturatedFat) HKQuantityTypeIdentifier.dietaryFatPolyunsaturated
Cholesterol Cholesterol intake HealthDataType.cholesterol (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.cholesterol) HKQuantityTypeIdentifier.dietaryCholesterol
Fiber & Sugar
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Dietary Fiber Dietary fiber intake HealthDataType.dietaryFiber (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.dietaryFiber) HKQuantityTypeIdentifier.dietaryFiber
Sugar Sugar intake HealthDataType.sugar (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.sugar) HKQuantityTypeIdentifier.dietarySugar
Minerals
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Calcium Calcium intake HealthDataType.calcium (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.calcium) HKQuantityTypeIdentifier.dietaryCalcium
Iron Iron intake HealthDataType.iron (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.iron) HKQuantityTypeIdentifier.dietaryIron
Magnesium Magnesium intake HealthDataType.magnesium (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.magnesium) HKQuantityTypeIdentifier.dietaryMagnesium
Manganese Manganese intake HealthDataType.manganese (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.manganese) HKQuantityTypeIdentifier.dietaryManganese
Phosphorus Phosphorus intake HealthDataType.phosphorus (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.phosphorus) HKQuantityTypeIdentifier.dietaryPhosphorus
Potassium Potassium intake HealthDataType.potassium (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.potassium) HKQuantityTypeIdentifier.dietaryPotassium
Selenium Selenium intake HealthDataType.selenium (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.selenium) HKQuantityTypeIdentifier.dietarySelenium
Sodium Sodium intake HealthDataType.sodium (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.sodium) HKQuantityTypeIdentifier.dietarySodium
Zinc Zinc intake HealthDataType.zinc (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.zinc) HKQuantityTypeIdentifier.dietaryZinc
B Vitamins
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Thiamin (B1) Thiamin (vitamin B1) intake HealthDataType.thiamin (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.thiamin) HKQuantityTypeIdentifier.dietaryThiamin
Riboflavin (B2) Riboflavin (vitamin B2) HealthDataType.riboflavin (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.riboflavin) HKQuantityTypeIdentifier.dietaryRiboflavin
Niacin (B3) Niacin (vitamin B3) intake HealthDataType.niacin (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.niacin) HKQuantityTypeIdentifier.dietaryNiacin
Pantothenic Acid (B5) Pantothenic acid (vitamin B5) HealthDataType.pantothenicAcid (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.pantothenicAcid) HKQuantityTypeIdentifier.dietaryPantothenicAcid
Vitamin B6 Vitamin B6 intake HealthDataType.vitaminB6 (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminB6) HKQuantityTypeIdentifier.dietaryVitaminB6
Biotin (B7) Biotin (vitamin B7) intake HealthDataType.biotin (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.biotin) HKQuantityTypeIdentifier.dietaryBiotin
Folate (B9) Folate (vitamin B9) intake HealthDataType.folate (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.folate) HKQuantityTypeIdentifier.dietaryFolate
Vitamin B12 Vitamin B12 intake HealthDataType.vitaminB12 (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminB12) HKQuantityTypeIdentifier.dietaryVitaminB12
Other Vitamins
Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Vitamin A Vitamin A intake HealthDataType.vitaminA (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminA) HKQuantityTypeIdentifier.dietaryVitaminA
Vitamin C Vitamin C intake HealthDataType.vitaminC (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminC) HKQuantityTypeIdentifier.dietaryVitaminC
Vitamin D Vitamin D intake HealthDataType.vitaminD (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminD) HKQuantityTypeIdentifier.dietaryVitaminD
Vitamin E Vitamin E intake HealthDataType.vitaminE (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminE) HKQuantityTypeIdentifier.dietaryVitaminE
Vitamin K Vitamin K intake HealthDataType.vitaminK (iOS HealthKit Only) Sum NutritionRecord (NutritionRecord.vitaminK) HKQuantityTypeIdentifier.dietaryVitaminK

🧘 Wellness

Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Mindfulness Session Meditation and mindfulness sessions HealthDataType.mindfulnessSession Sum MindfulnessSessionRecord HKCategoryTypeIdentifier.mindfulSession

πŸͺ· Cycle Tracking

Data Type Description Data Type Supported Aggregation Android Health Connect API iOS HealthKit API
Cervical Mucus Cervical mucus observations for fertility HealthDataType.cervicalMucus - CervicalMucusRecord HKCategoryTypeIdentifier.cervicalMucusQuality
Basal Body Temperature Basal body temperature HealthDataType.basalBodyTemperature Avg, Min, Max BasalBodyTemperatureRecord HKQuantityTypeIdentifier.basalBodyTemperature
Menstruation Flow Menstrual flow intensity HealthDataType.menstrualFlow - MenstruationFlowRecord HKCategoryTypeIdentifier.menstrualFlow
Ovulation Test Ovulation test result HealthDataType.ovulationTest - OvulationTestRecord HKCategoryTypeIdentifier.ovulationTestResult
Intermenstrual Bleeding Intermenstrual bleeding spotting HealthDataType.intermenstrualBleeding - - HKCategoryTypeIdentifier.persistentIntermenstrualBleeding

πŸ”„ Migration Guides #

  • Migration Guide from v1.x.x to v2.x.x

🀝 Contributing #

Contributions are welcome! See our GitHub Issues to report bugs or request features.

πŸ“„ License #

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

12
likes
0
points
1.12k
downloads

Publisher

unverified uploader

Weekly Downloads

The most comprehensive Flutter health plugin for seamless iOS HealthKit and Android Health Connect integration.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, health_connector_core, health_connector_hc_android, health_connector_hk_ios, health_connector_logger, meta

More

Packages that depend on health_connector