# 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()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#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 %}

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.

Go to the **SDK Reference** to find more details on the [Generate the Mobile SDK Auth Token **API Endpoint**](/reference/health-and-fitness-api/sdk-references.md)**.**

{% 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**
   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.

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](/health-and-fitness-api/mobile-only-sources/flutter.md) 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](/reference/health-and-fitness-api/readme.md#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](/reference/health-and-fitness-api/sdk-references/flutter.md) 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="/files/HWBv7PWMxBEc0WMNdWrW" alt="" data-size="line">**Apple Health -** iOS
2. <img src="/files/qO7xuPhRY1YEKm096Aq2" alt="" data-size="line"> **Samsung Health -** Android
3. <img src="/files/zhkGsxfmF1M2jUWRUXRA" alt="" data-size="line"> **Google Fit & Health Connect**- Android.

Note: the [Health & Fitness API](/health-and-fitness-api/getting-started.md) 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](/health-and-fitness-api/getting-started.md).
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tryterra.co/health-and-fitness-api/mobile-only-sources/flutter.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
