Health Connect (Android)
Google Health Connect is Android’s centralized health data repository. Unlike iOS’s built-in HealthKit, Health Connect may not be pre-installed on all Android devices. The health plugin provides APIs to check availability, request installation, and manage Health Connect permissions.
Checking Health Connect Status
Section titled “Checking Health Connect Status”getHealthConnectSdkStatus()
Section titled “getHealthConnectSdkStatus()”Get the current Health Connect SDK availability status.
Future<HealthConnectSdkStatus?> getHealthConnectSdkStatus()Returns
Section titled “Returns”Returns HealthConnectSdkStatus enum or null on error:
| Status | Value | Description |
|---|---|---|
sdkAvailable |
3 | Health Connect is installed and ready |
sdkUnavailableProviderUpdateRequired |
2 | Needs Health Connect provider update |
sdkUnavailable |
1 | Health Connect not available |
Example
Section titled “Example”final status = await health.getHealthConnectSdkStatus();
switch (status) { case HealthConnectSdkStatus.sdkAvailable: print('Health Connect is ready'); break; case HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired: print('Health Connect needs update'); break; case HealthConnectSdkStatus.sdkUnavailable: print('Health Connect not installed'); break; case null: print('Error checking status');}Status Caching
Section titled “Status Caching”The plugin caches the status in health.healthConnectSdkStatus:
// Check cached statusHealthConnectSdkStatus currentStatus = health.healthConnectSdkStatus;
// Refresh statusawait health.getHealthConnectSdkStatus();HealthConnectSdkStatus updatedStatus = health.healthConnectSdkStatus;Checking Availability
Section titled “Checking Availability”isHealthConnectAvailable()
Section titled “isHealthConnectAvailable()”Simple boolean check if Health Connect is available.
Future<bool> isHealthConnectAvailable()Returns
Section titled “Returns”true: Health Connect is installed and readyfalse: Health Connect unavailable or needs update
Example
Section titled “Example”bool available = await health.isHealthConnectAvailable();
if (!available) { // Prompt user to install _showHealthConnectInstallDialog();}Best Practice
Section titled “Best Practice”Always check before any health operations:
Future<void> ensureHealthConnectReady() async { if (!await health.isHealthConnectAvailable()) { throw HealthException( null, 'Health Connect is not available. Please install it first.', ); }}
Future<void> fetchHealthData() async { await ensureHealthConnectReady();
// Now safe to proceed final data = await health.getHealthDataFromTypes(...);}Installing Health Connect
Section titled “Installing Health Connect”installHealthConnect()
Section titled “installHealthConnect()”Open the app store to install or update Health Connect.
Future<void> installHealthConnect()Behavior
Section titled “Behavior”- Opens Google Play Store to Health Connect app
- User must manually install/update
- Returns immediately after opening store
- No return value or success indicator
Example: Installation Flow
Section titled “Example: Installation Flow”Future<void> setupHealthConnect() async { // Check availability final available = await health.isHealthConnectAvailable();
if (!available) { // Show dialog explaining Health Connect final shouldInstall = await showDialog<bool>( context: context, builder: (context) => AlertDialog( title: Text('Install Health Connect'), content: Text( 'This app requires Health Connect to access health data. ' 'Would you like to install it now?', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('Cancel'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text('Install'), ), ], ), );
if (shouldInstall == true) { await health.installHealthConnect(); } }}Example: Status-Based Installation
Section titled “Example: Status-Based Installation”Future<void> handleHealthConnectStatus() async { final status = await health.getHealthConnectSdkStatus();
switch (status) { case HealthConnectSdkStatus.sdkAvailable: // Ready to use break;
case HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired: // Need update showDialog( context: context, builder: (context) => AlertDialog( title: Text('Update Required'), content: Text('Please update Health Connect to continue.'), actions: [ TextButton( onPressed: () { health.installHealthConnect(); Navigator.pop(context); }, child: Text('Update'), ), ], ), ); break;
case HealthConnectSdkStatus.sdkUnavailable: case null: // Need installation showDialog( context: context, builder: (context) => AlertDialog( title: Text('Health Connect Required'), content: Text('Please install Health Connect to use this app.'), actions: [ TextButton( onPressed: () { health.installHealthConnect(); Navigator.pop(context); }, child: Text('Install'), ), ], ), ); break; }}Historical Data Access
Section titled “Historical Data Access”Health Connect allows reading historical data beyond 30 days if explicitly authorized.
isHealthDataHistoryAvailable()
Section titled “isHealthDataHistoryAvailable()”Check if the device supports historical data access.
Future<bool> isHealthDataHistoryAvailable()Returns
Section titled “Returns”true: Device supports historical data permissionfalse: Feature not available (older devices/SDK)
Example
Section titled “Example”bool historyAvailable = await health.isHealthDataHistoryAvailable();
if (historyAvailable) { print('Can request historical data access');} else { print('Historical data not supported on this device');}isHealthDataHistoryAuthorized()
Section titled “isHealthDataHistoryAuthorized()”Check if historical data permission has been granted.
Future<bool> isHealthDataHistoryAuthorized()Returns
Section titled “Returns”true: Historical data access grantedfalse: Not granted or feature unavailable
Example
Section titled “Example”bool hasHistory = await health.isHealthDataHistoryAuthorized();
if (!hasHistory) { // Request permission await health.requestHealthDataHistoryAuthorization();}requestHealthDataHistoryAuthorization()
Section titled “requestHealthDataHistoryAuthorization()”Request permission to access historical health data (>30 days old).
Future<bool> requestHealthDataHistoryAuthorization()Returns
Section titled “Returns”true: Permission grantedfalse: Permission denied or error
Example: Complete Historical Data Setup
Section titled “Example: Complete Historical Data Setup”Future<bool> setupHistoricalAccess() async { // 1. Check if feature is available final available = await health.isHealthDataHistoryAvailable(); if (!available) { print('Historical data not supported'); return false; }
// 2. Check current authorization final authorized = await health.isHealthDataHistoryAuthorized(); if (authorized) { print('Already authorized'); return true; }
// 3. Request permission final granted = await health.requestHealthDataHistoryAuthorization();
if (granted) { print('Historical access granted'); } else { print('User denied historical access'); }
return granted;}Use Case: Long-Term Analysis
Section titled “Use Case: Long-Term Analysis”Future<void> fetchLongTermData() async { // Ensure historical access final hasHistoricalAccess = await health.isHealthDataHistoryAuthorized();
if (!hasHistoricalAccess) { final granted = await health.requestHealthDataHistoryAuthorization(); if (!granted) { throw Exception('Historical data access required for this feature'); } }
// Now can fetch data from any time period final data = await health.getHealthDataFromTypes( types: [HealthDataType.WEIGHT], startTime: DateTime.now().subtract(Duration(days: 365)), // 1 year ago endTime: DateTime.now(), );
print('Fetched ${data.length} historical data points');}Background Data Access
Section titled “Background Data Access”Health Connect supports reading health data in the background.
isHealthDataInBackgroundAvailable()
Section titled “isHealthDataInBackgroundAvailable()”Check if background data access is supported.
Future<bool> isHealthDataInBackgroundAvailable()Returns
Section titled “Returns”true: Background access feature availablefalse: Not supported
Example
Section titled “Example”bool backgroundAvailable = await health.isHealthDataInBackgroundAvailable();
if (backgroundAvailable) { print('Can request background data access');}isHealthDataInBackgroundAuthorized()
Section titled “isHealthDataInBackgroundAuthorized()”Check if background data permission has been granted.
Future<bool> isHealthDataInBackgroundAuthorized()Returns
Section titled “Returns”true: Background access grantedfalse: Not granted or unavailable
Example
Section titled “Example”bool hasBackground = await health.isHealthDataInBackgroundAuthorized();
if (!hasBackground) { await health.requestHealthDataInBackgroundAuthorization();}requestHealthDataInBackgroundAuthorization()
Section titled “requestHealthDataInBackgroundAuthorization()”Request permission to read health data in the background.
Future<bool> requestHealthDataInBackgroundAuthorization()Returns
Section titled “Returns”true: Permission grantedfalse: Permission denied or error
Example: Background Sync Setup
Section titled “Example: Background Sync Setup”Future<bool> setupBackgroundSync() async { // 1. Check availability final available = await health.isHealthDataInBackgroundAvailable(); if (!available) { print('Background access not supported'); return false; }
// 2. Check current status final authorized = await health.isHealthDataInBackgroundAuthorized(); if (authorized) { print('Background access already granted'); return true; }
// 3. Request permission final granted = await health.requestHealthDataInBackgroundAuthorization();
if (granted) { print('Background access granted'); // Initialize background worker _initializeBackgroundWorker(); } else { print('Background access denied'); }
return granted;}Use Case: Periodic Sync
Section titled “Use Case: Periodic Sync”Future<void> initializeHealthSync() async { // Request background permission final hasBackgroundAccess = await health.isHealthDataInBackgroundAuthorized();
if (!hasBackgroundAccess) { final granted = await health.requestHealthDataInBackgroundAuthorization(); if (!granted) { print('Background sync not available - using foreground only'); return; } }
// Setup WorkManager or similar for background tasks Workmanager().registerPeriodicTask( 'health-sync', 'syncHealthData', frequency: Duration(hours: 1), );}Complete Health Connect Setup
Section titled “Complete Health Connect Setup”Comprehensive initialization flow:
class HealthConnectManager { final Health health;
HealthConnectManager(this.health);
Future<HealthConnectSetupResult> initialize() async { // Step 1: Check availability final available = await health.isHealthConnectAvailable(); if (!available) { return HealthConnectSetupResult.notInstalled; }
// Step 2: Request basic permissions final types = [ HealthDataType.STEPS, HealthDataType.HEART_RATE, HealthDataType.WEIGHT, ];
final authorized = await health.requestAuthorization(types); if (!authorized) { return HealthConnectSetupResult.permissionsDenied; }
// Step 3: Request historical data (optional) if (await health.isHealthDataHistoryAvailable()) { await health.requestHealthDataHistoryAuthorization(); }
// Step 4: Request background access (optional) if (await health.isHealthDataInBackgroundAvailable()) { await health.requestHealthDataInBackgroundAuthorization(); }
return HealthConnectSetupResult.success; }
Future<void> promptInstallation(BuildContext context) async { final result = await showDialog<bool>( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text('Health Connect Required'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.health_and_safety, size: 64, color: Colors.blue), SizedBox(height: 16), Text( 'This app uses Health Connect to access and manage your health data securely.', textAlign: TextAlign.center, ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('Cancel'), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), child: Text('Install Health Connect'), ), ], ), );
if (result == true) { await health.installHealthConnect(); } }}
enum HealthConnectSetupResult { success, notInstalled, permissionsDenied, error,}Error Handling
Section titled “Error Handling”All Health Connect operations should check availability first:
Future<T> withHealthConnectCheck<T>( Future<T> Function() operation,) async { // Check Health Connect availability if (!await health.isHealthConnectAvailable()) { throw UnsupportedError( 'Health Connect is not available. Please install it first.', ); }
// Perform operation return await operation();}
// Usagetry { final data = await withHealthConnectCheck(() { return health.getHealthDataFromTypes( types: [HealthDataType.STEPS], startTime: DateTime.now().subtract(Duration(days: 7)), endTime: DateTime.now(), ); });} on UnsupportedError catch (e) { // Handle Health Connect not available await health.installHealthConnect();} catch (e) { // Handle other errors print('Error: $e');}Platform Type Detection
Section titled “Platform Type Detection”Check if running on Health Connect platform:
HealthPlatformType platformType = health.platformType;
if (platformType == HealthPlatformType.googleHealthConnect) { // Android-specific code final status = await health.getHealthConnectSdkStatus(); print('Health Connect status: ${status?.name}');} else if (platformType == HealthPlatformType.appleHealth) { // iOS-specific code print('Using Apple HealthKit');}See Also
Section titled “See Also”- Permission Management - Requesting and managing permissions
- Platform Setup (Android) - AndroidManifest configuration
- Reading Data - Fetching health data