Basics
Future<List<HealthDataPoint>> getHealthDataFromTypes({ required List<HealthDataType> types, required DateTime startTime, required DateTime endTime, Map<HealthDataType, HealthDataUnit>? preferredUnits, List<RecordingMethod> recordingMethodsToFilter = const [],})Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
types |
List<HealthDataType> |
Yes | Health data types to query |
startTime |
DateTime |
Yes | Start of time range (inclusive) |
endTime |
DateTime |
Yes | End of time range (inclusive) |
preferredUnits |
Map<HealthDataType, HealthDataUnit>? |
No | Preferred units per type (iOS only) |
recordingMethodsToFilter |
List<RecordingMethod> |
No | Recording methods to exclude |
Return Value
Section titled “Return Value”Returns List<HealthDataPoint> containing:
- Raw data points from native health platform
- Automatically deduplicated
- Sorted by date (implementation-dependent)
Each HealthDataPoint includes:
value: Health value (numeric, workout, audiogram, etc.)type: Data typeunit: Measurement unitdateFrom/dateTo: Time rangesourcePlatform: iOS or AndroidsourceDeviceId: Device identifiersourceId/sourceName: Data source inforecordingMethod: How data was recordedmetadata: Platform-specific metadataworkoutSummary: For workout datadeviceModel: Device name (iOS only)
Basic Examples
Section titled “Basic Examples”Single Type Query
Section titled “Single Type Query”final heartRatePoints = await health.getHealthDataFromTypes( types: [HealthDataType.HEART_RATE], startTime: DateTime.now().subtract(Duration(hours: 24)), endTime: DateTime.now(),);
print('Found ${heartRatePoints.length} heart rate measurements');
for (var point in heartRatePoints) { final value = (point.value as NumericHealthValue).numericValue; print('Heart rate: $value ${point.unit.name} at ${point.dateTo}');}Multiple Types Query
Section titled “Multiple Types Query”final vitalSigns = await health.getHealthDataFromTypes( types: [ HealthDataType.HEART_RATE, HealthDataType.BLOOD_OXYGEN, HealthDataType.RESPIRATORY_RATE, HealthDataType.BODY_TEMPERATURE, ], startTime: DateTime.now().subtract(Duration(days: 7)), endTime: DateTime.now(),);
// Group by typefinal byType = <HealthDataType, List<HealthDataPoint>>{};for (var point in vitalSigns) { byType.putIfAbsent(point.type, () => []).add(point);}
print('Heart rate: ${byType[HealthDataType.HEART_RATE]?.length ?? 0} points');print('Blood oxygen: ${byType[HealthDataType.BLOOD_OXYGEN]?.length ?? 0} points');Advanced Features
Section titled “Advanced Features”Preferred Units (iOS)
Section titled “Preferred Units (iOS)”On iOS, you can request data in specific units:
final data = await health.getHealthDataFromTypes( types: [ HealthDataType.HEIGHT, HealthDataType.WEIGHT, HealthDataType.DISTANCE_WALKING_RUNNING, ], startTime: start, endTime: end, preferredUnits: { HealthDataType.HEIGHT: HealthDataUnit.CENTIMETER, // Instead of meters HealthDataType.WEIGHT: HealthDataUnit.POUND, // Instead of kg HealthDataType.DISTANCE_WALKING_RUNNING: HealthDataUnit.MILE, // Instead of meters },);Filter by Recording Method
Section titled “Filter by Recording Method”Exclude data based on how it was recorded:
// Exclude manual entriesfinal autoData = await health.getHealthDataFromTypes( types: [HealthDataType.STEPS, HealthDataType.DISTANCE_WALKING_RUNNING], startTime: start, endTime: end, recordingMethodsToFilter: [RecordingMethod.manual],);
// Exclude unknown sourcesfinal knownData = await health.getHealthDataFromTypes( types: [HealthDataType.HEART_RATE], startTime: start, endTime: end, recordingMethodsToFilter: [RecordingMethod.unknown],);
// Android: Exclude multiple methodsfinal activeOnlyData = await health.getHealthDataFromTypes( types: [HealthDataType.WORKOUT], startTime: start, endTime: end, recordingMethodsToFilter: [ RecordingMethod.manual, RecordingMethod.unknown, ],);Value Types
Section titled “Value Types”Different health types return different value structures:
Numeric Values
Section titled “Numeric Values”Most health metrics are numeric:
final data = await health.getHealthDataFromTypes( types: [HealthDataType.WEIGHT], startTime: start, endTime: end,);
for (var point in data) { final numericValue = point.value as NumericHealthValue; print('Weight: ${numericValue.numericValue} ${point.unit.name}');}Workout Values
Section titled “Workout Values”Workouts have detailed activity information:
final workouts = await health.getHealthDataFromTypes( types: [HealthDataType.WORKOUT], startTime: start, endTime: end,);
for (var point in workouts) { final workout = point.value as WorkoutHealthValue;
print('Activity: ${workout.workoutActivityType.name}'); print('Duration: ${point.dateTo.difference(point.dateFrom)}');
if (workout.totalEnergyBurned != null) { print('Calories: ${workout.totalEnergyBurned} ${workout.totalEnergyBurnedUnit?.name}'); }
if (workout.totalDistance != null) { print('Distance: ${workout.totalDistance} ${workout.totalDistanceUnit?.name}'); }
if (workout.totalSteps != null) { print('Steps: ${workout.totalSteps}'); }
// Check workout summary if (point.workoutSummary != null) { print('Summary: ${point.workoutSummary}'); }}Nutrition Values
Section titled “Nutrition Values”Nutrition data includes macro and micronutrients:
final meals = await health.getHealthDataFromTypes( types: [HealthDataType.NUTRITION], startTime: start, endTime: end,);
for (var point in meals) { final nutrition = point.value as NutritionHealthValue;
print('Meal: ${nutrition.name ?? "Unknown"}'); print('Type: ${nutrition.mealType ?? "Unknown"}');
if (nutrition.calories != null) { print('Calories: ${nutrition.calories} kcal'); } if (nutrition.protein != null) { print('Protein: ${nutrition.protein}g'); } if (nutrition.carbs != null) { print('Carbs: ${nutrition.carbs}g'); } if (nutrition.fat != null) { print('Fat: ${nutrition.fat}g'); }}Audiogram Values (iOS)
Section titled “Audiogram Values (iOS)”Hearing test results:
final audiograms = await health.getHealthDataFromTypes( types: [HealthDataType.AUDIOGRAM], startTime: start, endTime: end,);
for (var point in audiograms) { final audiogram = point.value as AudiogramHealthValue;
print('Frequencies: ${audiogram.frequencies}'); print('Left ear: ${audiogram.leftEarSensitivities} dB'); print('Right ear: ${audiogram.rightEarSensitivities} dB');}ECG Values (iOS)
Section titled “ECG Values (iOS)”Electrocardiogram data:
final ecgs = await health.getHealthDataFromTypes( types: [HealthDataType.ELECTROCARDIOGRAM], startTime: start, endTime: end,);
for (var point in ecgs) { final ecg = point.value as ElectrocardiogramHealthValue;
print('Voltage readings: ${ecg.voltageValues.length}'); print('Average heart rate: ${ecg.averageHeartRate} BPM'); print('Sampling frequency: ${ecg.samplingFrequency} Hz'); print('Classification: ${ecg.classification?.name}');
// Access individual voltage readings for (var voltage in ecg.voltageValues.take(5)) { print(' ${voltage.timeSinceSampleStart}s: ${voltage.voltage}V'); }}Time Range Patterns
Section titled “Time Range Patterns”Recent Data
Section titled “Recent Data”// Last hourfinal lastHour = await health.getHealthDataFromTypes( types: [HealthDataType.HEART_RATE], startTime: DateTime.now().subtract(Duration(hours: 1)), endTime: DateTime.now(),);
// Last 24 hoursfinal last24h = await health.getHealthDataFromTypes( types: [HealthDataType.STEPS], startTime: DateTime.now().subtract(Duration(hours: 24)), endTime: DateTime.now(),);
// Last 7 daysfinal lastWeek = await health.getHealthDataFromTypes( types: [HealthDataType.WEIGHT], startTime: DateTime.now().subtract(Duration(days: 7)), endTime: DateTime.now(),);Calendar-Based Ranges
Section titled “Calendar-Based Ranges”final now = DateTime.now();
// Today (since midnight)final today = await health.getHealthDataFromTypes( types: [HealthDataType.STEPS], startTime: DateTime(now.year, now.month, now.day), endTime: now,);
// This week (Monday to now)final weekday = now.weekday; // 1 = Monday, 7 = Sundayfinal thisWeekStart = now.subtract(Duration(days: weekday - 1));final thisWeek = await health.getHealthDataFromTypes( types: [HealthDataType.ACTIVE_ENERGY_BURNED], startTime: DateTime(thisWeekStart.year, thisWeekStart.month, thisWeekStart.day), endTime: now,);
// This monthfinal thisMonth = await health.getHealthDataFromTypes( types: [HealthDataType.WORKOUT], startTime: DateTime(now.year, now.month, 1), endTime: now,);
// This yearfinal thisYear = await health.getHealthDataFromTypes( types: [HealthDataType.WEIGHT], startTime: DateTime(now.year, 1, 1), endTime: now,);Custom Ranges
Section titled “Custom Ranges”// Specific date rangefinal custom = await health.getHealthDataFromTypes( types: [HealthDataType.BLOOD_PRESSURE_SYSTOLIC], startTime: DateTime(2024, 1, 1), endTime: DateTime(2024, 12, 31),);
// Between two timestampsfinal between = await health.getHealthDataFromTypes( types: [HealthDataType.HEART_RATE], startTime: DateTime.parse('2024-06-01T00:00:00'), endTime: DateTime.parse('2024-06-30T23:59:59'),);Data Processing
Section titled “Data Processing”Calculate Statistics
Section titled “Calculate Statistics”Future<HealthStats> calculateStats( List<HealthDataPoint> data,) async { if (data.isEmpty) { return HealthStats.empty(); }
final values = data .map((p) => (p.value as NumericHealthValue).numericValue.toDouble()) .toList();
values.sort();
final sum = values.reduce((a, b) => a + b); final mean = sum / values.length; final min = values.first; final max = values.last; final median = values[values.length ~/ 2];
return HealthStats( count: values.length, sum: sum, mean: mean, min: min, max: max, median: median, );}
// Usagefinal heartRate = await health.getHealthDataFromTypes( types: [HealthDataType.HEART_RATE], startTime: DateTime.now().subtract(Duration(days: 7)), endTime: DateTime.now(),);
final stats = await calculateStats(heartRate);print('Average heart rate: ${stats.mean.toStringAsFixed(1)} BPM');print('Range: ${stats.min} - ${stats.max} BPM');Group by Date
Section titled “Group by Date”Map<DateTime, List<HealthDataPoint>> groupByDate( List<HealthDataPoint> data,) { final grouped = <DateTime, List<HealthDataPoint>>{};
for (var point in data) { final date = DateTime( point.dateTo.year, point.dateTo.month, point.dateTo.day, );
grouped.putIfAbsent(date, () => []).add(point); }
return grouped;}
// Usagefinal steps = await health.getHealthDataFromTypes( types: [HealthDataType.STEPS], startTime: DateTime.now().subtract(Duration(days: 30)), endTime: DateTime.now(),);
final byDate = groupByDate(steps);
for (var entry in byDate.entries) { final dailyTotal = entry.value .map((p) => (p.value as NumericHealthValue).numericValue) .reduce((a, b) => a + b);
print('${entry.key.toLocal()}: $dailyTotal steps');}Filter by Source
Section titled “Filter by Source”List<HealthDataPoint> filterBySource( List<HealthDataPoint> data, String sourceName,) { return data.where((p) => p.sourceName == sourceName).toList();}
// Get all Apple Watch datafinal allData = await health.getHealthDataFromTypes(...);final watchData = filterBySource(allData, 'Apple Watch');Platform-Specific Behavior
Section titled “Platform-Specific Behavior”iOS: Sleep Data
Section titled “iOS: Sleep Data”Sleep types auto-convert to minutes based on time range:
final sleep = await health.getHealthDataFromTypes( types: [ HealthDataType.SLEEP_ASLEEP, HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_REM, HealthDataType.SLEEP_DEEP, ], startTime: DateTime.now().subtract(Duration(days: 1)), endTime: DateTime.now(),);
for (var point in sleep) { final minutes = (point.value as NumericHealthValue).numericValue; print('${point.type.name}: ${minutes} minutes'); print(' From: ${point.dateFrom}'); print(' To: ${point.dateTo}');}Android: BMI Calculation
Section titled “Android: BMI Calculation”On Android, BMI is computed from weight and height:
// Requesting BMI automatically fetches weight and heightfinal bmiData = await health.getHealthDataFromTypes( types: [HealthDataType.BODY_MASS_INDEX], startTime: start, endTime: end,);
// BMI is calculated using last observed height// and all weight measurements in the rangeError Handling
Section titled “Error Handling”Future<List<HealthDataPoint>> fetchDataSafely({ required List<HealthDataType> types, required DateTime start, required DateTime end,}) async { try { // Validate time range if (start.isAfter(end)) { throw ArgumentError('Start time must be before end time'); }
// Check permissions final hasPerms = await health.hasPermissions(types); if (hasPerms != true) { final granted = await health.requestAuthorization(types); if (!granted) { throw StateError('Permissions denied'); } }
// Fetch data final data = await health.getHealthDataFromTypes( types: types, startTime: start, endTime: end, );
return data; } on HealthException catch (e) { print('Health error for ${e.dataType}: ${e.cause}'); rethrow; } on PlatformException catch (e) { print('Platform error: ${e.code} - ${e.message}'); rethrow; } catch (e) { print('Unexpected error: $e'); rethrow; }}Performance Tips
Section titled “Performance Tips”- Limit time ranges: Smaller ranges = faster queries
- Be specific with types: Only request what you need
- Use filtering: Filter by recording method to reduce data
- Cache results: Store recent queries in memory
- Paginate large ranges: Break into chunks for year+ queries
See Also
Section titled “See Also”- Interval Reading - Time-bucketed data
- Aggregate Reading - Multi-type summaries
- Data Types - All available health data types
- API: HealthDataPoint - Full data model reference