Learn how to request and manage health data permissions
Health data is sensitive. Both iOS and Android require explicit user consent before your app can access or write health information.
Before your app can read or write health data, users must grant permission for each specific data type. Think of it like giving your app a key to access certain parts of the health data vault.
final types = [HealthDataType.STEPS];final granted = await health.requestAuthorization(types);
2
Check Result
See if the user granted permission
Copy
if (granted) { print('✓ Permission granted!'); // Now you can read data} else { print('✗ Permission denied');}
3
Read or Write Data
Use the health data
Copy
final data = await health.getHealthDataFromTypes( types: [HealthDataType.STEPS], startDate: yesterday, endDate: now,);
Example:
Copy
import 'package:health/health.dart';Future<void> requestStepsPermission() async { final health = Health(); await health.configure(); // Request permission to read steps final types = [HealthDataType.STEPS]; final granted = await health.requestAuthorization(types); if (granted) { print('✓ Can now read step data!'); } else { print('✗ User denied permission'); }}
Start with just one or two data types (like STEPS) to keep things simple when learning.
Check if your app already has permission before requesting or reading data.
Copy
// Check if we can read stepsfinal types = [HealthDataType.STEPS];final hasPermission = await health.hasPermissions(types);if (hasPermission == true) { print('✓ We have permission!');} else { print('Need to request permission'); await health.requestAuthorization(types);}
The return values are as follow:
Value
Meaning
true
✓ All permissions granted
false
✗ At least one permission denied or not granted
null
❓ Cannot determine (iOS READ permissions)
On iOS, checking READ permissions always returns null due to privacy. Always assume you need to request if you get null.
Ask for permissions when the user first uses a health feature:
Copy
Future<void> initializeHealth() async { // Check if we've asked before final prefs = await SharedPreferences.getInstance(); final askedBefore = prefs.getBool('health_permission_asked') ?? false; if (!askedBefore) { await health.requestAuthorization([HealthDataType.STEPS]); await prefs.setBool('health_permission_asked', true); }}
Request Before Reading
Always check and request before trying to read data:
Copy
Future<List<HealthDataPoint>> getStepsData() async { // Check permission first final hasPermission = await health.hasPermissions( [HealthDataType.STEPS], ); if (hasPermission != true) { // Request if not granted final granted = await health.requestAuthorization( [HealthDataType.STEPS], ); if (!granted) { throw Exception('Permission denied'); } } // Now safe to read return await health.getHealthDataFromTypes(...);}
Granular Requests
Request only what you need, when you need it:
Copy
// Request steps for main screenawait health.requestAuthorization([HealthDataType.STEPS]);// Later, when user opens workout featureawait health.requestAuthorization([HealthDataType.WORKOUT]);// When user wants to log weightawait health.requestAuthorization( [HealthDataType.WEIGHT], permissions: [HealthDataAccess.READ_WRITE],);
Users are more likely to grant permissions if you explain why you need them first!
Some iOS health data can only be read, not written by apps:
Copy
final readOnlyTypes = [ HealthDataType.ELECTROCARDIOGRAM, // Apple Watch only HealthDataType.HIGH_HEART_RATE_EVENT, // Apple Watch only HealthDataType.LOW_HEART_RATE_EVENT, // Apple Watch only HealthDataType.IRREGULAR_HEART_RATE_EVENT, // Apple Watch only HealthDataType.WALKING_HEART_RATE, // Calculated by iOS HealthDataType.GENDER, // User profile data HealthDataType.BLOOD_TYPE, // User profile data HealthDataType.BIRTH_DATE, // User profile data];// Always use READ for these typesawait health.requestAuthorization( readOnlyTypes, permissions: readOnlyTypes.map((t) => HealthDataAccess.READ).toList(),);
Requesting WRITE access for read-only types will throw an ArgumentError.
import 'package:permission_handler/permission_handler.dart';Future<bool> requestActivityRecognition() async { final status = await Permission.activityRecognition.request(); return status.isGranted;}// Use before requesting health dataawait requestActivityRecognition();await health.requestAuthorization([HealthDataType.STEPS]);
Future<bool> requestLocationPermission() async { final status = await Permission.location.request(); return status.isGranted;}// Use before requesting workout dataawait requestLocationPermission();await health.requestAuthorization([HealthDataType.WORKOUT]);
Problem: User denied permissions and they can’t be requested again.Solution: Guide users to Settings:
Copy
showDialog( context: context, builder: (context) => AlertDialog( title: Text('Enable Health Access'), content: Text( '1. Open Settings app\n' '2. Scroll to ${yourAppName}\n' '3. Tap Health\n' '4. Enable the data types you want to share' ), actions: [ TextButton( onPressed: () => OpenSettings.openSettings(), child: Text('Open Settings'), ), ], ),);
Health Connect Not Available
Problem: Health Connect returns unavailable status.Solution:
Copy
final status = await health.getHealthConnectSdkStatus();switch (status) { case HealthConnectSdkStatus.SDK_UNAVAILABLE: // Not installed - offer to install await health.installHealthConnect(); break; case HealthConnectSdkStatus.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED: // Needs update showUpdateDialog(); break; case HealthConnectSdkStatus.SDK_AVAILABLE: // All good! break;}
hasPermissions Returns Null
Problem: Permission check returns null on iOS.Solution: This is expected behavior for privacy. Always request permissions:
Copy
final hasPermission = await health.hasPermissions(types);// On iOS, hasPermission will be null for READif (hasPermission != true) { // Request regardless await health.requestAuthorization(types);}
import 'package:permission_handler/permission_handler.dart';// Request before requesting health authorizationawait Permission.activityRecognition.request();// Then request health permissionsawait health.requestAuthorization([HealthDataType.STEPS]);