# Flutter

## Overview

This guide will walk you through the necessary steps to use the Terra Flutter Plugin with Android and Apple-based integrations. It covers everything from SDK initialization, user connection, permission handling, and background data updates.

The Terra Flutter Plugin supports the following integrations:

* Samsung Health (Android)
* Health Connect (Android)
* Apple Health (iOS)

***

## 1. Install and Setup Terra Flutter

1. Install the terra flutter package using `flutter pub get terra_flutter_bridge`
2. Complete the following iOS and/or Android Setup

{% tabs %}
{% tab title="iOS Setup" %}

1. In your terminal, cd to your `/ios` folder, and run `pod install` to install all the dependencies.
2. **Add Capabilities:**
   1. Healthkit > Healthkit Background Delivery
   2. Background Modes > Background processing
   3. Background modes > Background fetch
3. **Add the following keys to your info.plist:**

#### **Method 1: Using XCode**

* 1\) Go to the `/ios` directory of your project, open the `.xcworkspace` in XCode.
* 2\) Go to ***info.plist,*** and add the following keys and values:

<table><thead><tr><th width="370.9501953125">Key</th><th>Value</th></tr></thead><tbody><tr><td>Privacy - Health Share Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Privacy - Health Records Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Privacy - Health Update Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Permitted background task scheduler</td><td><code>co.tryterra.data.post.request</code></td></tr></tbody></table>

#### **Method 2: Directly editing info.plist**

* 1\) In your app project, go to your `/ios` folder
* 2\) Go to ***info.plist,*** and add the following tags:

{% code title="info.plist" lineNumbers="true" %}

```xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
  <string>co.tryterra.data.post.request</string>
</array>
  
<key>NSHealthClinicalHealthRecordsShareUsageDescription</key>
<string>Using TerraiOS to gather health data</string>

<key>NSHealthShareUsageDescription</key>
<string>Using TerraiOS as a mean of getting Health Data</string>

<key>NSHealthUpdateUsageDescription</key>
<string>Allow writing data to health kit</string>
```

{% endcode %}
{% endtab %}

{% tab title="Android Setup" %}

## Access Samsung Health

Terra has a privileged partnership with Samsung that gives you direct access to the Samsung Health SDK — no Health Connect intermediary needed. This is the recommended route for Samsung devices.

**1. Apply for Samsung Health access**

Submit your application through the [Samsung Health partnership portal](https://developer.samsung.com/SHealth/business-partner/m48wvqi1mt9w2w4c).

{% hint style="info" %}
While waiting for approval, you can start development immediately using the **Health Connect** route below. Switching to Samsung direct later is just a version bump — no code changes needed.
{% endhint %}

**2. Install the Samsung-tagged SDK**

After approval, install the Samsung-tagged version of the Terra SDK:

* **React Native:** `npm install terra-react@<samsung-version>` — find Samsung-tagged versions on the [npm versions page](https://www.npmjs.com/package/terra-react?activeTab=versions)
* **Flutter:** Use the Samsung-tagged version of `terra_flutter_bridge` — find versions on [pub.dev](https://pub.dev/packages/terra_flutter_bridge/versions)

**3. Add ProGuard rule (required for release builds)**

In your `android/app/proguard-rules.pro`, add:

```proguard
-keep class com.samsung.android.** { *; }
```

Without this, R8 minification will strip Samsung SDK classes and cause runtime crashes in production.

**4. Requirements**

* **Samsung Health** app must be installed on the device
* **Android 28 (minSDK 28)** and above
* Enable **Developer Mode** in Samsung Health on test devices (Settings > About Samsung Health > tap version number repeatedly)
* No additional manifest or Gradle changes needed — the Terra SDK handles everything

***

## Access Health Connect

#### **1. In the Health Connect app**

Give all permissions between the apps you wish to read from (e.g. **Samsung Health, Google Fit, etc**) & Health Connect.

#### **2. Add Health Connect capability to your app**

In your app project, go to the `/android` folder.

Include the permission tags under the Activity you wish to link the user to when they click the **privacy policy** link in the Health Connect permission screen. Here are the steps:

1. Go to your Android App's `AndroidManifest.xml`
2. Go to your **Privacy Policy** <mark style="color:purple;">`<activity>`</mark> and include the following tags under this <mark style="color:purple;">`<activity>`</mark> tag.

"Your Android manifest needs to have an Activity that displays your app's **privacy policy**, which is your app's rationale of the requested permissions, describing how the user's data is used and handled." — Health Connect.

<pre class="language-xml" data-title="AndroidManifest.xml" data-line-numbers><code class="lang-xml"><strong>&#x3C;intent-filter>
</strong>  &#x3C;action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE"/>
 &#x3C;/intent-filter>
  
&#x3C;intent-filter>
   &#x3C;action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
  &#x3C;category android:name="android.intent.category.HEALTH_PERMISSIONS"/>
&#x3C;/intent-filter>
</code></pre>

{% hint style="warning" %}

### Apply for Health Connect access

Before going **live** (release), you will need to apply for permissions to access the Health Connect API with Google.

1. Use this [application form](https://developer.android.com/health-and-fitness/guides/health-connect/publish/declare-access#explain-use-data-types).
2. For each permission which you are ***not*** using, please add the following lines to your AndroidManifest.xml

{% code title="AndroidManifest.xml" overflow="wrap" %}

```xml
<uses-permission android:name="android.permission.health.READ_HEART_RATE" tools:node="remove"/>
```

{% endcode %}

with `android.permission.health.XXX` for each permission you aren't using
{% endhint %}
{% endtab %}
{% endtabs %}

***

## 2. Initialize the SDK

The first step is to **initialize the Terra SDK.**

The initialization only needs to be **done once on app start**, (e.g. in your <mark style="color:red;">`main.dart`</mark> or an equivalent file), **and** **every time** the app is brought into the foreground. This ensures that the SDK is properly set up and ready to be used.

{% hint style="warning" %}
The SDK should be initialized every time your app is opened.

This is a necessary prerequisite for other SDK functions to run as expected.
{% endhint %}

#### **Step 1: Import the Terra Plugin**

In your project, you should now be able to import function from the Terra Plugin. Here is an example:

{% code lineNumbers="true" %}

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
```

{% endcode %}

#### **Step 2: Initialize the Terra SDK**

In order to interact with the SDK, you need to call `initTerra` first.

Call `initTerra()` with the following arguments:

* `devId`: Your **Developer ID** provided by Terra.
* `referenceId`: An **ID of your choice** to identify your app user.

Hereis an example:

{% code title="initializeTerra.dart" lineNumbers="true" %}

```dart
import 'package:flutter/material.dart';
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
import 'package:terra_flutter_bridge/models/enums.dart';
import 'package:terra_flutter_bridge/models/responses.dart';

void main() async {
  // Ensure that Flutter bindings are initialized
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Terra once when the app starts
  await _initializeTerra();

  // Launch your app thereafter
  runApp(Container());  // Empty container or basic widget if needed
}

Future<void> _initializeTerra() async {
  String devID = 'YOUR_DEV_ID';
  String referenceID = 'YOUR_REFERENCE_ID';

  SuccessMessage? result = await TerraFlutter.initTerra(devID, referenceID);

  if (result?.error != null) {
    throw Exception('Failed to initialise Terra ${result?.error}');
  }
  // Successfully initialised Terra!
}
```

{% endcode %}

(**N.B** This call is asynchronous, please ensure this is complete before using other SDK functions).

***

## 3. Connect a User

#### 1. Call [`initConnection()`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/health-and-fitness-api/sdk-references/ios-swift#initconnection)

Import `initConnection` and call the function with the following arguments:

* `type`: Specify the connection type — `Connection.appleHealth`, `Connection.samsung`, or `Connection.healthConnect`
* `token`: A **one-time authentication token** generated from your backend server. This ensures secure communication between your app and the Terra servers.
* `customPermissions`: A **set of permissions** that define what data you want to request (e.g., heart rate, steps). If empty, it defaults to all available permissions.
* `schedulerOn`:
  * **iOS**: This parameter has no effect. Background delivery is controlled by calling `Terra.setUpBackgroundDelivery()` in your AppDelegate — see [Step 5](#id-5.-background-delivery-setup-ios-only) below.
  * **Android**: To allow Terra to make scheduled requests whenever the app is in the foreground.

{% tabs %}
{% tab title="Apple Health (iOS)" %}
{% code title="initialiseConnection.dart" lineNumbers="true" %}

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
import 'package:terra_flutter_bridge/models/enums.dart';
import 'package:terra_flutter_bridge/models/responses.dart';

Future<void> _initialiseConnection() async {
  String token = 'example_token';

  SuccessMessage? result = await TerraFlutter.initConnection(
    Connection.appleHealth,
    token,
    true,   // schedulerOn — no effect on iOS, see setUpBackgroundDelivery() below
    [],     // customPermissions — empty defaults to all
  );

  if (result?.error != null) {
    throw Exception('Failed to initialise connection ${result?.error}');
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="Samsung Health (Android)" %}
{% code title="initialiseConnection.dart" lineNumbers="true" %}

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
import 'package:terra_flutter_bridge/models/enums.dart';
import 'package:terra_flutter_bridge/models/responses.dart';

Future<void> _initialiseConnection() async {
  String token = 'example_token';

  SuccessMessage? result = await TerraFlutter.initConnection(
    Connection.samsung,
    token,
    true,   // schedulerOn — enables foreground scheduler
    [],     // customPermissions — empty defaults to all
  );

  if (result?.error != null) {
    throw Exception('Failed to initialise connection ${result?.error}');
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="Health Connect (Android)" %}
{% code title="initialiseConnection.dart" lineNumbers="true" %}

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
import 'package:terra_flutter_bridge/models/enums.dart';
import 'package:terra_flutter_bridge/models/responses.dart';

Future<void> _initialiseConnection() async {
  String token = 'example_token';

  SuccessMessage? result = await TerraFlutter.initConnection(
    Connection.healthConnect,
    token,
    true,   // schedulerOn — enables foreground scheduler
    [],     // customPermissions — empty defaults to all
  );

  if (result?.error != null) {
    throw Exception('Failed to initialise connection ${result?.error}');
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}

### Apple Health Kit Permission Screen: `initConnection()`

**Apple Health** only shows the permission popup **once**, so calling `initConnection()` multiple times won’t trigger the popup again unless:

a. you call initConnection with **an expanded set of** `customPermissions`

b. the **app is deleted & reinstalled.**
{% endhint %}

{% hint style="info" %}

### **`initConnection` only needs to be called a single time.**

**Health Connect** prohibits the permission popup to appear more than once for any given permission, so calling `initConnection` more than once will result in no action at all

The only case where it would re-appear is if:

* you call initConnection with **an expanded set of `customPermissions`**
* the **app is deleted & reinstalled.**
* **A permission that was not granted to use on release by Google has been requested by the app**
  {% endhint %}

{% hint style="info" %}

### **Apple Health Kit Permission Screen: WebViews** 🚧

* Apple HealthKit implements the permissions popup as a **WebView.**
* If your app is also based on a **WebView**, you will need to interrupt your **WebView**, call `initConnection`, then upon completion re-open your **WebView.**
  {% endhint %}

#### 2. Generate an Auth Token

To be able to call the `initConnection()` method, you need to pass a `token` as an argument.

This `token` is a **single-use token** created to ensure the authentication endpoint for creating a connection (and connecting the SDK to Terra's servers) does not get abused.

Generate the token with this endpoint <mark style="color:orange;">`POST`</mark>`https://api.tryterra.co/v2/auth/generateAuthToken` . Make sure to call it with your Terra `x-api-key` and `dev-id` in the headers from **your backend server.** After you generate the token, provide the response to your client side using your own logic.&#x20;

Go to the **SDK Reference** to find more details on the [Generate the Mobile SDK Auth Token **API Endpoint**](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/health-and-fitness-api/sdk-references)**.**

{% hint style="warning" %}

* During the development phase, it it acceptable to place this call on the **client** side, exposing your API key in your mobile app.
* For a production implementation, **DO NOT** expose your API key this way, and make sure to **only** make this call from your **backend.**
  {% endhint %}

***

## 4. Validate the Connection

{% hint style="info" %}

## Always validate the connection before using the SDK

Check if a `user_id` exists right after **initializing the Terra SDK** to see if the connection still exists.

1. **Check if the User is Connected**&#x20;
   1. If the function returns a user ID, the user is still connected, 🎉 keep vibing along!
   2. If the function returns <mark style="color:red;">`nil`</mark>, the user needs to reconnect.
2. **Re-connect if Needed**\
   If the connection is lost, you can call `terra.initConnection()` again to re-establish the connection.&#x20;

Calling `terra.initConnection()` when the user is already connected or just needs a reconnection will not trigger any permission screens from Apple Health, and the user flow will remain uninterrupted. The connection will be re-established if necessary.
{% endhint %}

***

## 5. Background Delivery setup (iOS only)

For Apple apps, you can enable background delivery settings to allow data to be synced even when your app is not brought to the foreground.

{% tabs %}
{% tab title="Apple Health" %}

1. Go to your `/ios` folder in the **Flutter** **project**
2. Call the function `setUpBackgroundDelivery` in your AppDelegate's `didFinishLaunchingWithOptions` function

This will ensure you get updates for the user's Apple Health data automatically sent to your destination.

{% code title="AppDelegate.swift" lineNumbers="true" %}

```swift
import UIKit
import Flutter
import TerraiOS

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
      Terra.setUpBackgroundDelivery()
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
```

{% endcode %}

{% hint style="success" %}

## iOS Background Delivery Behaviour:

* Data will still be triggered if the **app** **is** **killed** (with much lower frequency)
* Data will only be triggered when **phone** **is** **unlocked**
* Data can only be triggered where there is **network connection**
* Only enabled **Data** **Types** from [the Terra Dashboard](https://dashboard.tryterra.co) will be triggered by background delivery.
* If you are using **CustomPermissions,** please ensure the following permissions are enabled for Background Delivery to work correctly:
  * For **Daily: CustomPermissions.STEPS**
  * For **Sleep: CustomPermissions.SLEEP\_ANALYSIS**
  * For **Body: CustomPermissions.BMI, CustomPermissions.HEART\_RATE**
  * For **Activity: CustomPermissions.WORKOUT\_TYPE**
  * For **Nutrition: CustomPermissions.NUTRITION\_CALORIES**
    {% endhint %}
    {% endtab %}
    {% endtabs %}

***

## 6. Filtering by source app (iOS, optional)

Apple Health aggregates data from every health app on the user's device. If a user has both a cloud-based Terra connection (e.g. WHOOP, Garmin) and the same provider's companion app syncing into Apple Health, you'll receive duplicate data — once from the cloud API and once from the Apple Health SDK.

Use `setIgnoredSources` to tell the SDK to skip data from specific apps in HealthKit. Call this **once on every app launch**, after `TerraFlutter.initTerra()` completes. It is not persisted across app restarts. On Android, this function is a no-op.

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';

// After TerraFlutter.initTerra() completes:
await TerraFlutter.setIgnoredSources(["com.whoop.app", "com.garmin.connect.mobile"]);
```

Common bundle identifiers:

| App            | Bundle identifier           |
| -------------- | --------------------------- |
| WHOOP          | `com.whoop.app`             |
| Garmin Connect | `com.garmin.connect.mobile` |
| Fitbit         | `com.fitbit.FitbitMobile`   |
| Oura           | `com.ouraring.oura`         |

{% hint style="info" %}
To find a specific app's bundle identifier, have the user check **Settings → Health → Data Access & Devices** on their iPhone.
{% endhint %}

***

Now you'll start receiving health data events **automatically** to your Data Destination (e.g. webhook)!

You can also request historical data to backfill, to verify that data exists, or as a fallback.

Check out the [Flutter SDK reference](https://docs.tryterra.co/health-and-fitness-api/mobile-only-sources/flutter) for details about all the functions in the SDK.

***

## Disconnecting a user

In order to disconnect an SDK user, you may use the [same endpoint as for Web API-based integrations](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/health-and-fitness-api/readme#delete-auth-deauthenticateuser), called from your **backend**.

***

{% code title="pollDaily.dart" lineNumbers="true" %}

```dart
import 'package:terra_flutter_bridge/terra_flutter_bridge.dart';
import 'package:terra_flutter_bridge/models/enums.dart';
import 'package:terra_flutter_bridge/models/responses.dart';

Future<void> getData() async {
  // Example connection type and dates
  Connection connection = Connection.appleHealth;  // Assuming Connection is an enum with APPLE
  DateTime startDate = DateTime.now().subtract(Duration(days: 1));  // Replace with actual start date
  DateTime endDate = DateTime.now();                                // Replace with actual end date
  
  // Optionally, send data to webhook or not
  bool toWebhook = false;  // Set to false to receive the data in the response

  // Call the getDaily function
  DataMessage? dataMessage = await TerraFlutter.getDaily(connection, startDate, endDate, toWebhook: toWebhook);

  // Handle the response based on the structure of DataMessage
  if (dataMessage?.error != null) {
    throw Exception("Error getting data ${dataMessage?.error}");
  }
  
  // Process data here: `dataMessage?.data`
}
```

{% endcode %}

Check out the [Flutter SDK reference](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/health-and-fitness-api/sdk-references/flutter) for details about all the functions in the SDK

***

{% hint style="warning" %}

## Data Sources requiring the Terra **Mobile-SDK**

The **Mobile SDK** is <mark style="color:red;">**ONLY**</mark> used to connect to the following integrations:

1. <img src="https://464213908-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVGMJVuZnZyOtvV4b53cY%2Fuploads%2FDH2JWDtg632mUQlEdrLH%2F4p3k173m8ihe71872bi9r0292q-0af5626e815091005bacb793985c1870.png?alt=media&#x26;token=062c917e-02fe-4029-9809-fc881cf09de3" alt="" data-size="line">**Apple Health -** iOS
2. <img src="https://464213908-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVGMJVuZnZyOtvV4b53cY%2Fuploads%2F0lTv9KpldVVjBdg3JJQs%2Fimgbin_samsung-galaxy-samsung-health-samsung-electronics-android-png.png?alt=media&#x26;token=b58f3118-a2e1-4b09-9d30-4f862253adb6" alt="" data-size="line"> **Samsung Health -** Android
3. <img src="https://464213908-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVGMJVuZnZyOtvV4b53cY%2Fuploads%2FmotL0wnZKiEWOKt46zOU%2Fvecteezy_google-fit-icon-logo-symbol_22484515.png?alt=media&#x26;token=a2d0ae73-5116-49fd-8ce1-0f7b5e6f716b" alt="" data-size="line"> **Google Fit & Health Connect**- Android.&#x20;

Note: the [Health & Fitness API](https://docs.tryterra.co/health-and-fitness-api) is the preferred way to connect to Google Fit due to better reliability.

For **ALL** other integrations, please refer to Integration Setup the [Health & Fitness API](https://docs.tryterra.co/health-and-fitness-api).
{% endhint %}
