health_connector 1.4.0
health_connector: ^1.4.0 copied to clipboard
A Flutter plugin to access health data from Health Connect (Android) and HealthKit (iOS)
health_connector #
📖 Overview #
health_connector provides a unified, type-safe API for accessing health data across Android and
iOS platforms. It abstracts platform differences while exposing platform-specific features when
needed.
✨ Features #
| Feature | Description |
|---|---|
| 🔐 Permission Management | Request/check/revoke permissions |
| 📖 Reading Health Data | Read a single health record by ID or multiple health records within a specified date/time range with pagination |
| ✍️ Writing Health Data | Write health records |
| 🔄 Updating Health Records | Modify existing record |
| 🗑️ Deleting Health Records | Remove specific records by their IDs or within a date/time range |
| ➕ Aggregating Health Data | Sum/Avg/Min/Max Aggregation |
Explore the plugin's capabilities using the Health Connector Toolbox example app:
| Permission Request | Read Health Data | Write Health Data | Aggregate Health Data |
|---|---|---|---|
|
|
|
|
🎯 Requirements #
- Flutter >=3.3.0
- Dart >=3.9.2
- Android:
- minSdkVersion: 26 (Android 8.0)
- compileSdk: 34+
- iOS:
- iOS >=15.0
- Xcode >=14.0
🚀 Getting Started #
📦 Installation #
Add health_connector to your project:
flutter pub add health_connector
Or manually add to your pubspec.yaml:
dependencies:
health_connector: [latest_version]
⚙️ Platform Setup #
For platform-specific setup instructions, see:
- Android: See health_connector_hc_android
- iOS: See health_connector_hk_ios
📚 Usage #
Import Package
import 'package:health_connector/health_connector.dart';
Check Platform Availability
final status = await HealthConnector.getHealthPlatformStatus();
switch (status) {
case HealthPlatformStatus.available:
print('Health platform ready');
break;
case HealthPlatformStatus.installationOrUpdateRequired:
print('Please install or update health platform');
break;
case HealthPlatformStatus.unavailable:
print('Health platform not supported');
break;
}
Create HealthConnector Instance
final connector = await HealthConnector.create(
HealthConnectorConfig(
isLoggerEnabled: true, // Enable debug logging
),
);
// Check which platform is being used
print('Platform: ${connector.healthPlatform.name}');
// Prints: "healthConnect" on Android, "appleHealth" on iOS
Permissions
Request Permissions
final permissions = [
HealthDataType.steps.readPermission,
HealthDataType.steps.writePermission,
HealthDataType.weight.readPermission,
HealthDataType.weight.writePermission,
// ...
];
final results = await connector.requestPermissions(permissions);
for (final result in results) {
print('${result.permission}: ${result.status}');
}
Important: On iOS, read permissions always return
PermissionStatus.unknownfor privacy reasons. This prevents apps from detecting if a user has any health data.
Read Health Records
Read Health Record by ID
final recordId = HealthRecordId('existing-record-id');
final readRequest = HealthDataType.steps.readRecord(recordId);
final record = await connector.readRecord(readRequest);
if (record != null) {
print('Steps: ${record.count.value} from ${record.startTime} to ${record.endTime}');
} else {
print('Record not found');
}
Read Multiple Health Records
// Read step records for the last 7 days
final request = HealthDataType.steps.readRecords(
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} from ${record.startTime} to ${record.endTime}');
}
Read Multiple Health Records with Pagination
// Initialize pagination request
var request = HealthDataType.steps.readRecords(
startTime: DateTime.now().subtract(Duration(days: 30)),
endTime: DateTime.now(),
pageSize: 100, // Request 100 records per page
);
var allRecords = <StepRecord>[];
var pageNumber = 1;
print('Starting pagination with pageSize: 100');
// Paginate through all records
while (true) {
print('\n--- Page $pageNumber ---');
// Fetch the current page
final response = await connector.readRecords(request);
print('Records returned: ${response.records.length}');
print('Has nextPageRequest: ${response.nextPageRequest != null}');
// Add records to our collection
allRecords.addAll(response.records.cast<StepRecord>());
print('Total records collected so far: ${allRecords.length}');
// Platform-specific pagination handling
if (response.nextPageRequest == null) {
print('No nextPageRequest - pagination complete');
break;
}
// Move to next page
request = response.nextPageRequest!;
pageNumber++;
}
print('\n=== Pagination Complete ===');
print('Total records fetched: ${allRecords.length}');
print('Total pages processed: $pageNumber');
Write Health Records
Write Single Health Record
final stepRecord = StepRecord(
id: HealthRecordId.none, // Must be none for new records
startTime: DateTime.now().subtract(Duration(hours: 1)),
endTime: DateTime.now(),
count: Numeric(5000),
metadata: Metadata.automaticallyRecorded(
device: Device.fromType(DeviceType.phone),
),
);
final recordId = await connector.writeRecord(stepRecord);
print('Record written with ID: $recordId');
Write Multiple Health Records
final records = [
StepRecord(
id: HealthRecordId.none,
startTime: DateTime.now().subtract(Duration(hours: 2)),
endTime: DateTime.now().subtract(Duration(hours: 1)),
count: Numeric(3000),
metadata: Metadata.automaticallyRecorded(
device: Device.fromType(DeviceType.phone),
),
),
DistanceRecord(
id: HealthRecordId.none,
startTime: DateTime.now().subtract(Duration(hours: 1)),
endTime: DateTime.now(),
distance: Length(5000),
metadata: Metadata.automaticallyRecorded(
device: Device.fromType(DeviceType.phone),
),
),
// Other health records ...
];
final recordIds = await connector.writeRecords(records);
print('Wrote ${recordIds.length} records');
Update Health Records
// Read existing record
final recordId = HealthRecordId('existing-record-id');
final readRequest = HealthDataType.steps.readRecord(recordId);
final existingRecord = await connector.readRecord(readRequest);
if (existingRecord != null) {
// Create updated record
final updatedRecord = existingRecord.copyWith(
startTime: existingRecord.startTime,
endTime: existingRecord.endTime,
count: Numeric(existingRecord.count.value + 100),
metadata: existingRecord.metadata,
);
// Update returns ID (same on Android, new on iOS)
final newId = await connector.updateRecord(updatedRecord);
print('Original ID: $originalId');
print('New ID: $newId');
// originalId == newId on Android (originalId != newId on iOS)
}
Delete Health Records
Delete Health Records by IDs
await connector.deleteRecordsByIds(
dataType: HealthDataType.steps,
recordIds: [
HealthRecordId('id-1'),
HealthRecordId('id-2'),
],
);
print('Records deleted');
Delete Health Records by Date Range
await connector.deleteRecords(
dataType: HealthDataType.steps,
startTime: DateTime.now().subtract(Duration(days: 7)),
endTime: DateTime.now(),
);
print('Records deleted');
Aggregate Health Data
// Get total steps for today
final sumRequest = HealthDataType.steps.aggregateSum(
startTime: DateTime.now().startOfDay,
endTime: DateTime.now(),
);
final sumResponse = await connector.aggregate(sumRequest);
print('Total steps today: ${sumResponse.value.value}');
// Get average weight for the last month
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');
// Get min/max weight
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');
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');
⚒️ Platform Specific Features #
While health_connector provides a unified API across platforms, some features are only available
on specific platforms due to platform capabilities and limitations.
🤖 Android-only API #
Get All Granted Permissions
Retrieve a list of all permissions that have been granted to your app.
try {
final grantedPermissions = await connector.getGrantedPermissions();
print('Granted permissions: ${grantedPermissions.length}');
// Iterate through granted permissions
for (final permission in grantedPermissions) {
print('Permission: ${permission.permission} - Status: ${permission.status}');
}
} on HealthConnectorException catch (e) {
if (e.errorCode == HealthConnectorErrorCode.unsupportedHealthPlatformApi) {
print('This API is only available on Android');
} else {
print('Error: ${e.message}');
}
}
Revoke All Granted Permissions
Revoke all permissions that have been granted to your app.
try {
await connector.revokeAllPermissions();
print('All permissions revoked successfully');
} on HealthConnectorException catch (e) {
if (e.errorCode == HealthConnectorErrorCode.unsupportedHealthPlatformApi) {
print('This API is only available on Android');
} else {
print('Error: ${e.message}');
}
}
📋 Supported Health Data Types #
🏃 Activity #
📏 Body Measurements #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Weight | ✅ | ✅ | • Android: WeightRecord• iOS: HKQuantityTypeIdentifier.bodyMass |
| Height | ✅ | ✅ | • Android: HeightRecord• iOS: HKQuantityTypeIdentifier.height |
| Body Fat Percentage | ✅ | ✅ | • Android: BodyFatRecord• iOS: HKQuantityTypeIdentifier.bodyFatPercentage |
| Lean Body Mass | ✅ | ✅ | • Android: LeanBodyMassRecord• iOS: HKQuantityTypeIdentifier.leanBodyMass |
| Bone Mass | ❌ | ❌ | • Android: BoneMassRecord• iOS: Not available |
| Body Mass Index | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.bodyMassIndex |
| Waist Circumference | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.waistCircumference |
| Basal Metabolic Rate | ❌ | ❌ | • Android: BasalMetabolicRateRecord• iOS: Not available |
🩸 Cycle Tracking / Reproductive Health #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Menstruation Flow | ❌ | ❌ | • Android: MenstruationFlowRecord• iOS: HKCategoryTypeIdentifier.menstrualFlow |
| Cervical Mucus | ❌ | ❌ | • Android: CervicalMucusRecord• iOS: HKCategoryTypeIdentifier.cervicalMucusQuality |
| Ovulation Test | ❌ | ❌ | • Android: OvulationTestRecord• iOS: HKCategoryTypeIdentifier.ovulationTestResult |
| Basal Body Temperature | ❌ | ❌ | • Android: BasalBodyTemperatureRecord• iOS: HKQuantityTypeIdentifier.basalBodyTemperature |
| Sexual Activity | ❌ | ❌ | • Android: SexualActivityRecord• iOS: HKCategoryTypeIdentifier.sexualActivity |
| Intermenstrual Bleeding | ❌ | ❌ | • Android: Not available • iOS: HKCategoryTypeIdentifier.intermenstrualBleeding |
| Pregnancy Test Result | ❌ | ❌ | • Android: Not available • iOS: HKCategoryTypeIdentifier.pregnancyTestResult |
| Progesterone Test Result | ❌ | ❌ | • Android: Not available • iOS: HKCategoryTypeIdentifier.progesteroneTestResult |
🍎 Nutrition #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Hydration / Water | ✅ | ✅ | • Android: HydrationRecord• iOS: HKQuantityTypeIdentifier.dietaryWater |
| Nutrition / Dietary Energy | ✅ | ✅ | • Android: NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryEnergyConsumed |
| Dietary Protein | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryProtein |
| Dietary Carbohydrates | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryCarbohydrates |
| Dietary Fat Total | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFatTotal |
| Dietary Fat Saturated | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFatSaturated |
| Dietary Fat Polyunsaturated | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFatPolyunsaturated |
| Dietary Fat Monounsaturated | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFatMonounsaturated |
| Dietary Cholesterol | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryCholesterol |
| Dietary Sodium | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietarySodium |
| Dietary Fiber | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFiber |
| Dietary Sugar | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietarySugar |
| Dietary Calcium | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryCalcium |
| Dietary Iron | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryIron |
| Dietary Vitamin C | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminC |
| Dietary Vitamin D | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminD |
| Dietary Vitamin E | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminE |
| Dietary Vitamin K | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminK |
| Dietary Vitamin B6 | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminB6 |
| Dietary Vitamin B12 | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryVitaminB12 |
| Dietary Thiamin (B1) | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryThiamin |
| Dietary Riboflavin (B2) | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryRiboflavin |
| Dietary Niacin (B3) | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryNiacin |
| Dietary Folate | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryFolate |
| Dietary Biotin | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryBiotin |
| Dietary Pantothenic Acid | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryPantothenicAcid |
| Caffeine | ✅ | ✅ | • Android: Part of NutritionRecord• iOS: HKQuantityTypeIdentifier.dietaryCaffeine |
😴 Sleep #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Sleep Session/Stage | ✅ | ✅ | • Android: SleepSessionRecord• iOS: HKCategoryTypeIdentifier.sleepAnalysis |
❤️ Vitals #
🧘 Wellness / Mental Health #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Mindfulness Session | ❌ | ❌ | • Android: MindfulnessSessionRecord• iOS: HKCategoryTypeIdentifier.mindfulSession |
🏥 Clinical Records #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Electrocardiogram (ECG) | ❌ | ❌ | • Android: Not available • iOS: HKElectrocardiogram |
| Vision Prescription | ❌ | ❌ | • Android: Not available • iOS: HKVisionPrescription |
🎧 Audio Exposure #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Headphone Audio Exposure | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.headphoneAudioExposureEvent |
| Environmental Audio Exposure | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.environmentalAudioExposureEvent |
🚶 Mobility & Gait Analysis #
| Data Type | Android | iOS | Documentation |
|---|---|---|---|
| Walking Speed | ❌ | ❌ | • Android: Part of SpeedRecord• iOS: HKQuantityTypeIdentifier.walkingSpeed |
| Running Speed | ❌ | ❌ | • Android: Part of SpeedRecord• iOS: HKQuantityTypeIdentifier.runningSpeed |
| Step Length | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.stepLength |
| Walking Asymmetry Percentage | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.walkingAsymmetryPercentage |
| Walking Double Support Percentage | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.walkingDoubleSupportPercentage |
| Six Minute Walk Test Distance | ❌ | ❌ | • Android: Not available • iOS: HKQuantityTypeIdentifier.sixMinuteWalkTestDistance |
| Stair Ascent Speed | ❌ | ❌ | • Android: Part of SpeedRecord• iOS: HKQuantityTypeIdentifier.stairAscentSpeed |
| Stair Descent Speed | ❌ | ❌ | • Android: Part of SpeedRecord• iOS: HKQuantityTypeIdentifier.stairDescentSpeed |
🤝 Contributing #
Contributions are welcome!
To report issues or request features, please visit our GitHub Issues.