Skip to content
CARP Docs
Ask AISystemLightDark
On this page

Installation

Add carp_health_package to a CAMS app and configure Google Health Connect on Android and HealthKit on iOS.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

Setting up the package takes four stages: add the packages, configure Google Health Connect on Android, configure HealthKit on iOS, and register the package.

Add the packages

The package runs on CARP Mobile Sensing (CAMS), so complete the CAMS setup in Installation first. Then add the packages:

flutter pub add carp_core carp_mobile_sensing carp_health_package

carp_health_package exports the health plugin, so your app can use HealthDataType and the other plugin classes without a health dependency of its own.

Configure Android

On Android, the package reads from Health Connect. Add five settings to the CAMS setup:

  1. Set minSdk to 26 or later

    The health plugin and the Health Connect client need API level 26 (Android 8.0) or later, and Flutter projects default to 24. In android/app/build.gradle.kts, replace the default:

    android/app/build.gradle.kts
    android {
        defaultConfig {
            minSdk = flutter.minSdkVersion 
            minSdk = 26
            // ... the other defaultConfig settings
        }
    }

    The Health Connect client also needs compileSdk 36 or later and Android Gradle plugin 8.9.1 or later.

  2. Extend FlutterFragmentActivity

    The plugin opens the Health Connect permission screen with the AndroidX activity result API, which FlutterActivity does not support. Change the base class of MainActivity:

    android/app/src/main/kotlin/<package_path>/MainActivity.kt
    import io.flutter.embedding.android.FlutterActivity 
    import io.flutter.embedding.android.FlutterFragmentActivity 
    
    class MainActivity : FlutterActivity() 
    class MainActivity : FlutterFragmentActivity() 
  3. Declare the read permissions

    Health Connect grants access per record type, and it grants only the permissions that the app declares. Above <application>, declare the read permission of each data type that the protocol collects. For steps and heart rate:

    android/app/src/main/AndroidManifest.xml
    <uses-permission android:name="android.permission.health.READ_STEPS" />
    <uses-permission android:name="android.permission.health.READ_HEART_RATE" />

    CAMS only reads, so WRITE_STEPS and the other write permissions are not needed. For the permission of every data type, see Data types. To read data from more than 30 days before the participant granted access, see Request history access (Android).

    Warning

    The health service collects nothing until every data type is granted, and Health Connect cannot grant a permission that the manifest does not declare. Declare the permission of every data type in the protocol.

  4. Let the app find Health Connect

    On Android 11 or later, an app sees another app only when it declares it. Add Health Connect to the <queries> element that the Flutter template already contains:

    android/app/src/main/AndroidManifest.xml
    <queries>
        <!-- ... the existing PROCESS_TEXT intent -->
        <package android:name="com.google.android.apps.healthdata" />
        <intent>
            <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
        </intent>
    </queries>
  5. Link the Health Connect permission screen

    The Health Connect permission screen links to your app's privacy policy, and it opens your app through an intent filter or an activity alias. Inside the <activity> element of .MainActivity, add the intent filter:

    android/app/src/main/AndroidManifest.xml
    <intent-filter>
        <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
    </intent-filter>

    Inside <application>, add the activity alias:

    android/app/src/main/AndroidManifest.xml
    <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>

    Both open MainActivity, so the app must show its privacy policy, or lead to it, when it opens this way. Google may require this to approve an app that asks for health permissions.

For the Android setup of the health plugin itself, such as the Health Connect install flow, see Android. That page also declares write permissions, which CAMS does not need.

Configure iOS

On iOS, the package reads from Apple Health through HealthKit. The health plugin needs iOS 15.0 or later, which the CAMS setup already sets. It works with both Swift Package Manager and CocoaPods.

HealthKit access does not go through permission_handler, so the package needs no Podfile macros. Add two settings to the CAMS setup:

  1. Add the HealthKit capability

    Open ios/Runner.xcworkspace in Xcode, select Runner → Signing & Capabilities, click + Capability and add HealthKit. Xcode adds the com.apple.developer.healthkit entitlement to the app.

  2. Add the usage descriptions

    HealthKit shows the read description when it asks the participant for access. CAMS only reads, but the health plugin documents both keys, so add both to the top-level <dict>:

    ios/Runner/Info.plist
    <key>NSHealthShareUsageDescription</key>
    <string>This app reads your steps and heart rate from Apple Health for a research study.</string>
    <key>NSHealthUpdateUsageDescription</key>
    <string>This app does not write data to Apple Health.</string>

For the iOS setup of the health plugin itself, see iOS.

Register the sampling package

Register the package in main(), before you call configure() on SmartPhoneClientManager:

import 'package:carp_core/carp_core.dart' hide Smartphone;
import 'package:carp_mobile_sensing/carp_mobile_sensing.dart';
import 'package:carp_health_package/health_package.dart';

void main() {
  CarpMobileSensing.ensureInitialized();
  SamplingPackageRegistry().register(HealthSamplingPackage());

  // ... the rest of your app
}

Call CarpMobileSensing.ensureInitialized() before register(): registering a sampling package before CAMS is initialized throws a null check error.

Registering the package:

  • makes the dk.cachet.carp.health measure type known to CAMS;
  • adds the HealthService device and its HealthServiceManager, which asks for access and holds the health plugin that reads the data;
  • lets the app run each HealthAppTask as a task that the participant starts;
  • registers the package's classes for JSON, so that CAMS can load protocols and data that contain them.

configure() restores the studies saved in earlier runs of the app, so the package must be registered before it.

Troubleshooting

Build fails with a minSdkVersion error

Set minSdk to 26 or later in android/app/build.gradle.kts, as in Configure Android. The health plugin and the Health Connect client declare API level 26 as their minimum, so the manifest merge fails for a lower value.

Build fails with an AAR metadata error

Raise compileSdk to 36 or later in android/app/build.gradle.kts, and the com.android.application plugin to 8.9.1 or later in android/settings.gradle.kts. The Health Connect client requires both versions.

Log warns about Health Connect on SDK level below 34

Ask the participant to install Health Connect from Google Play, for example with Health().installHealthConnect(), which opens its store page. On Android 13 and earlier, Health Connect is a separate app, and CAMS logs this warning when it configures the health service.

Next step

Quickstart

Build an app that reads steps and heart rate every hour.

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture