2

I am trying to access step data using Flutter's health plugin, but I keep encountering the issue where permissions are not granted, even though I have declared the necessary permissions in my AndroidManifest

I have implemented the following function to request permissions and access step data. However, requestAuthorization() always returns false

Future<bool> _requestPermissions() async {
  try {
    // First check if Health Connect is available
    bool isHealthConnectAvailable = await health.isHealthConnectAvailable();
    print("Health Connect available: $isHealthConnectAvailable");

    if (!isHealthConnectAvailable) {
      print("Health Connect is not available on this device");
      return false;
    }

    // Define all the data types we want to access
    final types = [HealthDataType.STEPS];

    // Set up corresponding permissions
    final permissions =
        types.map((type) => HealthDataAccess.READ_WRITE).toList();

    print("Checking existing permissions for health data types: $types");
    // I thought if i had declared it in manifest i would get it true here. But apparently not
    // Check if we already have permissions

    bool? hasAllPermissions = await health.hasPermissions(types);

    print("Existing permissions status: $hasAllPermissions");

    // If we already have all permissions, no need to request them again
    if (hasAllPermissions == true) {
      print("Already have all required permissions!");
      return true;
    }

    // Request Activity Recognition permission
    var activityStatus = await Permission.activityRecognition.status;
    print("Activity Recognition initial status: ${activityStatus.name}");

    if (!activityStatus.isGranted) {
      final status = await Permission.activityRecognition.request();
      await Permission.locationWhenInUse.request();
      await Permission.sensors.request();
      print("Activity Recognition request result: ${status.name}");
      if (!status.isGranted) {
        print("Activity Recognition permission denied: ${status.name}");
        return false;
      }
    }

    print("Requesting authorization for health data types: $types");

    try {
      // Check if types are supported before requesting authorization
      for (HealthDataType type in types) {
        bool supported = await health.isDataTypeAvailable(type);
        print("Health data type $type supported: $supported");
        if (!supported) {
          print("Warning: $type is not supported on this device");
        }
      }

      // Request Health Connect permissions with both read and write access
      // always returns false
      bool authorized = await health.requestAuthorization(
        types,
        permissions: permissions,
      );

      print("Health authorization result: $authorized");

      if (!authorized) {
        print("Health permissions not granted!");
        return false;
      }

      // Verify if we can actually read the data types
      for (HealthDataType type in types) {
        bool isAvailable = await health.isDataTypeAvailable(type);
        print("Health data type $type available: $isAvailable");
      }

      print("Health permissions granted successfully!");
      return true;
    } catch (healthErr) {
      print("Error requesting Health API permissions: $healthErr");
      return false;
    }
  } catch (e) {
    print("General error in permission request: $e");
    return false;
  }
}

here is my android manifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required Permissions -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.BODY_SENSORS" />
    
    <!-- Health Connect Permissions -->
    <uses-permission android:name="android.permission.health.READ_STEPS" />
    <uses-permission android:name="android.permission.health.WRITE_STEPS" />
    <uses-permission android:name="android.permission.health.READ_DISTANCE" />
    <uses-permission android:name="android.permission.health.WRITE_DISTANCE" />
    <uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
    <uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" />
    
    <!-- Legacy Google Fit permissions (as fallback) -->
    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

    <!-- Queries for Health Connect -->
    <queries>
        <package android:name="com.google.android.apps.healthdata" />
        <intent>
            <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
        </intent>
    </queries>

    <application
        android:label="MyAppl"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        <!-- Main Activity -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">

            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme"
            />

            <!-- Main app intent filter -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <!-- Health Connect intent filter -->
            <intent-filter>
                <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
            </intent-filter>
        </activity>

        <!-- Health Connect Permission Usage Activity -->
        <activity-alias
            android:name=".ViewPermissionUsageActivity"
            android:exported="true"
            android:targetActivity=".MainActivity"
            android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
            <intent-filter>
                <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
                <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
            </intent-filter>
        </activity-alias>

        <!-- Flutter embedding -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

        <!-- Health Connect metadata -->
        <meta-data 
            android:name="health_permissions" 
            android:resource="@array/health_permissions" />
    </application>
</manifest>

Here is the requestAuthorization function:

Future<bool> requestAuthorization(
    List<HealthDataType> types, {
    List<HealthDataAccess>? permissions,
  }) async {
    await _checkIfHealthConnectAvailableOnAndroid();
    if (permissions != null && permissions.length != types.length) {
      throw ArgumentError(
          'The length of [types] must be same as that of [permissions].');
    }

    if (permissions != null) {
      for (int i = 0; i < types.length; i++) {
        final type = types[i];
        final permission = permissions[i];
        if ((type == HealthDataType.ELECTROCARDIOGRAM ||
                type == HealthDataType.HIGH_HEART_RATE_EVENT ||
                type == HealthDataType.LOW_HEART_RATE_EVENT ||
                type == HealthDataType.IRREGULAR_HEART_RATE_EVENT ||
                type == HealthDataType.WALKING_HEART_RATE ||
                type == HealthDataType.ATRIAL_FIBRILLATION_BURDEN) &&
            permission != HealthDataAccess.READ) {
          throw ArgumentError(
              'Requesting WRITE permission on ELECTROCARDIOGRAM / HIGH_HEART_RATE_EVENT / LOW_HEART_RATE_EVENT / IRREGULAR_HEART_RATE_EVENT / WALKING_HEART_RATE / ATRIAL_FIBRILLATION_BURDEN is not allowed.');
        }
      }
    }

    final mTypes = List<HealthDataType>.from(types, growable: true);
    final mPermissions = permissions == null
        ? List<int>.filled(types.length, HealthDataAccess.READ.index,
            growable: true)
        : permissions.map((permission) => permission.index).toList();

    // on Android, if BMI is requested, then also ask for weight and height
    if (Platform.isAndroid) _handleBMI(mTypes, mPermissions);

    List<String> keys = mTypes.map((e) => e.name).toList();
    final bool? isAuthorized = await _channel.invokeMethod(
        'requestAuthorization', {'types': keys, "permissions": mPermissions});
    return isAuthorized ?? false;
  }

I Ensured that:

  • Health Connect is installed on the device.
  • Ensured that AndroidManifest.xml contains the correct permissions.
  • Checked permission status before requesting new permissions.
  • Used health.hasPermissions() to verify existing permissions.
  • Manually granted permissions in the device settings, but still no success.

So my questions if anyone could help me with this issue are:

  • Why is requestAuthorization() always returning false?
  • Am I missing any additional setup required for Health Connect permissions?
  • Are there any specific device settings (I am using Samsung Galaxy 14 with Upside Down Cake) that could be causing this issue?
1
  • Edit 1: I have identified the issue behind the error, after debugging i noticed that final bool? isAuthorized = await _channel.invokeMethod( 'requestAuthorization', {'types': keys, "permissions": mPermissions}); doesn't allow permission launcher to work and gives out error like this: I/FLUTTER_HEALTH( 1319): Permission launcher not found I found a relevant github thread about this discussion if it helps: [link](github.com/cph-cachet/flutter-plugins/issues/908 ) Commented Mar 10 at 13:49

1 Answer 1

0

This github Link on the issue works for me:

[link](github.com/cph-cachet/flutter-plugins/issues/908)

TLDR;

basically you need to extend the FlutterFragmentActivity in MainActivity.kt file this bug is prevalent in flutter_health plugin and doesn't launch the permission handler which is required to collect specific data points like steps etc.


import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity()
Sign up to request clarification or add additional context in comments.

2 Comments

While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
All right I will update it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.