This package is a bug fixing of ticket cph-cachet/flutter-plugins#1072
My native iOS code not good but at least solved the problem. Please contribute to this plugin to make it can reuse in the future.
Download and put it in project same level as lib:
Create health_service.dart and copy-paste this code:
import 'dart:io';
import 'package:health/health.dart';
class HealthServices {
HealthServices._();
static HealthServices get instance => HealthServices._();
// create a HealthFactory for use in the app
HealthFactory health = HealthFactory(useHealthConnectIfAvailable: true);
List<HealthDataType> get types =>
Platform.isIOS ? [
HealthDataType.STEPS,
HealthDataType.ACTIVE_ENERGY_BURNED,
HealthDataType.EXERCISE_TIME,
HealthDataType.DISTANCE_WALKING_RUNNING,
] : [
HealthDataType.STEPS,
HealthDataType.ACTIVE_ENERGY_BURNED,
HealthDataType.HEART_RATE,
// HealthDataType.MOVE_MINUTES,
HealthDataType.DISTANCE_DELTA,
HealthDataType.WORKOUT,
];
Future<void> requestAuthorization() async {
FlutterBugfender.log("HealthService: requestAuthorization");
await health.requestAuthorization(types);
}
void logStepsRecordIf30Days() async {
for (int i = 0; i <= 10; i++) {
DateTime startDatePoint = DateTime.now()
.subtract(Duration(days: i))
.startOfTheDay();
DateTime endDatePoint = DateTime.now()
.subtract(Duration(days: i))
.endOfTheDay();
int? totalStepCount = await health.getTotalStepsInInterval(
startDatePoint, endDatePoint);
FlutterBugfender.log(
'Fetched STEP: $totalStepCount | from: $startDatePoint - to: $endDatePoint');
Logger.i(
'Fetched STEP: $totalStepCount | from: $startDatePoint - to: $endDatePoint');
}
}
void fetch7DaysData({
required DateTime start,
required DateTime end,
}) async {
try {
int totalSteps = 0;
double totalDistance = 0;
int totalCalories = 0;
double totalMinutes = 0;
List<HealthDataPoint> healthData = await health.getHealthDataFromTypes(
start,
end,
types,
);
for (var health in healthData) {
switch (health.type) {
case HealthDataType.STEPS:
totalSteps = (health.value as NumericHealthValue).numericValue.toInt();
print("totalSteps count: $totalSteps");
break;
case HealthDataType.ACTIVE_ENERGY_BURNED:
totalCalories = (health.value as NumericHealthValue).numericValue.toInt();
print("totalCalories count: $totalCalories");
break;
case HealthDataType.DISTANCE_WALKING_RUNNING:
totalDistance = (health.value as NumericHealthValue).numericValue.toDouble().meterToMiles;
print("totalDistance count: $totalDistance");
break;
case HealthDataType.EXERCISE_TIME:
totalMinutes = (health.value as NumericHealthValue).numericValue.toDouble();
print("totalMinutes count: $totalMinutes");
break;
default:
break;
}
}
ActivityModel resultActivity = ActivityModel(
steps: totalSteps,
distance: totalDistance.meterToMiles,
calorie: totalCalories.toInt(),
time: totalMinutes.toInt()
);
} catch (error, stackTrace) {
Logger.e(
'Failed to fetch health data: $error',
stackTrace: stackTrace,
);
}
}
Future<ActivityModel> fetchHealthData({
required DateTime start,
required DateTime end,
}) async {
try {
// requesting access to the data types before reading them
bool requested = await health.requestAuthorization(types);
if (!requested) {
Logger.e('User deny health permission');
return ActivityModel();
}
int totalSteps = 0;
double totalDistance = 0;
int totalCalories = 0;
// double totalMinutes = 0;
List<HealthDataPoint> stepsData = await health.getHealthDataFromTypes(
start,
end,
[HealthDataType.STEPS],
);
List<HealthDataPoint> caloriesData = await health.getHealthDataFromTypesCalories(
start,
end,
[HealthDataType.ACTIVE_ENERGY_BURNED],
);
List<HealthDataPoint> distanceData = await health.getHealthDataFromTypesDistance(
start,
end,
[HealthDataType.DISTANCE_WALKING_RUNNING],
);
// List<HealthDataPoint> exerciseData = await health.getHealthDataFromTypesExercise(
// start,
// end,
// [HealthDataType.EXERCISE_TIME],
// );
List<HealthDataPoint> allData = [...stepsData, ...caloriesData, ...distanceData];
for (var health in allData) {
switch (health.type) {
case HealthDataType.STEPS:
totalSteps = (health.value as NumericHealthValue).numericValue.toInt();
break;
case HealthDataType.ACTIVE_ENERGY_BURNED:
totalCalories = (health.value as NumericHealthValue).numericValue.toInt();
break;
case HealthDataType.DISTANCE_WALKING_RUNNING:
totalDistance = (health.value as NumericHealthValue).numericValue.toDouble().meterToMiles;
break;
// case HealthDataType.EXERCISE_TIME:
// totalMinutes = (health.value as NumericHealthValue).numericValue.toDouble();
// print("totalMinutes count: $totalMinutes");
// break;
default:
break;
}
}
// double totalDistance = await health.getTotalDistanceInInterval(
// start, end) ?? 0;
// double totalCalories = await health.getTotalCaloriesInInterval(
// start, end) ?? 0;
double totalMinutes = await health.getTotalMinutesInInterval(
start, end) ?? 0;
ActivityModel resultActivity = ActivityModel(
steps: totalSteps,
distance: totalDistance,
calorie: totalCalories.toInt(),
time: totalMinutes.toInt()
);
return resultActivity;
} catch (error, stackTrace) {
Logger.e(
'Failed to fetch health data: $error',
stackTrace: stackTrace,
);
return ActivityModel();
}
}
}
dayInterval = 1, if you want the interval by hour or second please adjust in native code.