---
title: "Instrument Flutter Mobile applications | Grafana Cloud documentation"
description: "Instrument Flutter applications with the Grafana Faro Flutter SDK."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Instrument Flutter Mobile applications

> Note
> 
> The Grafana Faro Flutter SDK, part of Observability for Mobile, is in Private Preview. To request access, add your information to the [Observability for Mobile preview wait list form](https://docs.google.com/forms/d/e/1FAIpQLSemWUuyeVwL7bepmuftEqcKj-aDarLJUgm7AqGBJq03S-wFGg/viewform).

The Grafana Faro Flutter SDK instruments Flutter applications on iOS and Android and sends real user monitoring telemetry to Grafana Cloud.

Use this guide to create a Mobile Observability application and install the Faro Flutter SDK. The examples use Faro Flutter SDK 0.17.0. The package requires Flutter 3.32.0 or later and Dart 3.8.0 or later.

The SDK doesn’t instrument Flutter web.

Grafana Cloud stores logs, events, exceptions, and measurements in Grafana Cloud Logs, and stores traces in Grafana Cloud Traces. Each signal includes `sdk_name` set to `faro-mobile-flutter`.

For configuration beyond this guide, refer to the [Faro Flutter SDK on pub.dev](https://pub.dev/packages/faro).

## Before you begin

Confirm the following before you instrument the application:

- A Grafana Cloud account ([sign up for free](/auth/sign-up/create-user))
- A Flutter application for iOS or Android

## Create a Mobile Observability application

The application collects and analyzes telemetry from your mobile app. You use its name to tell it apart from your other applications.

To create the application, follow these steps:

1. Sign in to your Grafana Cloud instance.
2. Click **Observability** &gt; **Frontend**.
3. Click **Create New** &gt; **Mobile Application**.
4. Enter an **Application name**.
5. Click **Next**.

The **Install and initialize SDK** step opens.

## Install and initialize the SDK

Select **Flutter**. The snippet on this step includes the `apiKey` and `collectorUrl` for the application you just created. Copy those two values into the sample below.

`apiKey` is the last segment of `collectorUrl`. If the URL ends in `/collect/123456789`, the API key is `123456789`.

### Add the dependency

The wizard shows `faro: <latest>`. In `pubspec.yaml`, set that line to the current version:

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
dependencies:
  faro: ^0.17.0
```

Install the package:

Bash ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```bash
flutter pub get
```

### Initialize Faro

Keep your existing root widget. In the sample, that widget is `MyApp`. Replace your current `main()` with this one, and keep the same `runApp` call you already use.

`appName` is the name you entered in the wizard. `appVersion` is your mobile app version. `appEnv` is `development`, `staging`, or `production`.

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

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

void main() {
  Faro().runApp(
    optionsConfiguration: FaroConfig(
      appName: 'my-flutter-app',
      appVersion: '1.0.0',
      appEnv: 'production',
      apiKey: '<APP_KEY>',
      collectorUrl: 'https://<FARO_COLLECTOR_HOST>/collect/<APP_KEY>',
    ),
    appRunner: () {
      runApp(const MyApp());
    },
  );
}
```

Leave `MyApp` and the rest of your widget tree as they are.

This `main()` collects Flutter errors, sessions, CPU and memory measurements, and cold and warm start times. It sends every session. It doesn’t attach a user until you call `setUser`.

### Add optional instrumentation

On **Additional configurations**, add any of the following to the `main()` above. Skip a block to leave that instrumentation off.

Each snippet is an edit to that `main()`, not a new file. A complete `main()` with every option is at the end of this section.

To track HTTP calls from Dart, add this import with the other imports:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
import 'dart:io';
```

Add this line as the first line inside `main()`, before `Faro().runApp()`:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
HttpOverrides.global = FaroHttpOverrides(HttpOverrides.current);
```

If the app already sets `HttpOverrides.global`, keep that override and pass it into `FaroHttpOverrides`.

HTTP tracking covers clients in the Dart layer, including `http` and `dio`. It doesn’t cover HTTP calls made directly from Android or iOS code. The SDK redacts credentials in telemetry URLs and the values of common sensitive query parameters, including `token`, `access_token`, `api_key`, and `password`. The request the app sends still uses the original URI.

To track screen changes, add `navigatorObservers` to your existing `MaterialApp`. Leave `home` set to the widget you already use:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
return MaterialApp(
  navigatorObservers: [FaroNavigationObserver()],
  home: myExistingHome,
);
```

Each screen change sends a `view_changed` event. For `GoRouter`, set `observers` instead of `navigatorObservers`:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
GoRouter(
  observers: [FaroNavigationObserver()],
);
```

To track taps and gestures, wrap your existing widget inside `appRunner`. If that call is `runApp(const MyApp())`, replace it with:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
runApp(
  const FaroUserInteractionWidget(child: MyApp()),
);
```

To also track asset loading, use this `runApp` call instead of the previous one:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
runApp(
  FaroAssetTracking(
    child: const FaroUserInteractionWidget(child: MyApp()),
  ),
);
```

To keep telemetry while the device is offline, add this immediately before `Faro().runApp()`:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
Faro().transports.add(
  OfflineTransport(
    maxCacheDuration: const Duration(days: 3),
  ),
);
```

Cached events older than `maxCacheDuration` are discarded when the device reconnects.

#### `main()` with every option

Use this `main()` when you want HTTP tracking, interactions, asset loading, and offline caching. It replaces the `main()` above and still runs your existing `MyApp`. Add `navigatorObservers` to your existing `MaterialApp`, as shown in the screen-change step.

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
import 'dart:io';

import 'package:faro/faro.dart';

void main() {
  HttpOverrides.global = FaroHttpOverrides(HttpOverrides.current);

  Faro().transports.add(
    OfflineTransport(
      maxCacheDuration: const Duration(days: 3),
    ),
  );

  Faro().runApp(
    optionsConfiguration: FaroConfig(
      appName: 'my-flutter-app',
      appVersion: '1.0.0',
      appEnv: 'production',
      apiKey: '<APP_KEY>',
      collectorUrl: 'https://<FARO_COLLECTOR_HOST>/collect/<APP_KEY>',
    ),
    appRunner: () {
      runApp(
        FaroAssetTracking(
          child: const FaroUserInteractionWidget(child: MyApp()),
        ),
      );
    },
  );
}
```

### Send custom data

Custom data is the last part of **Install and initialize SDK**. Call these methods from your app after `Faro().runApp()` has started.

Send an event when you need a signal the SDK doesn’t collect on its own. Event attributes are strings.

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
Faro().pushEvent('checkout-button-tapped', attributes: {
  'startedInBackgroundCount': '1',
  'from': 'notification',
});
```

Send a log with a severity:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
Faro().pushLog('My warn message', level: LogLevel.warn);
```

Send a measurement when the value is a number:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
Faro().pushMeasurement({'cpu_usage': 350}, 'app_cpu_usage');
```

Report an error you have already caught. `stackTrace` comes from the `catch` clause:

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
} catch (error, stackTrace) {
  Faro().pushError(
    type: 'my_custom_error',
    value: error.toString(),
    stacktrace: stackTrace,
  );
}
```

Associate telemetry with a user so you can filter sessions by the person using the app. Call `setUser` after authentication, and call `setUser(FaroUser.cleared())` on logout.

dart ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```dart
Faro().setUser(FaroUser(
  id: 'user-123',
  username: 'a.user',
  email: 'a.user@example.com',
));

// On logout:
Faro().setUser(FaroUser.cleared());
```

`persistUser` defaults to `true`, so the SDK restores the last user on the next launch.

Click **Continue**.

## Enable alerting

The **Enable alerting** step opens. Alerting is optional.

Select the rules you want, then click **Complete**. You can change the thresholds later on the application settings page.

Expand table

| Alert                    | When it fires                                                                     |
|--------------------------|-----------------------------------------------------------------------------------|
| **Errors Count**         | The number of errors increases day over day for the last 24 hours.                |
| **New Errors**           | New errors appear in the last 24 hours.                                           |
| **Slow App Start**       | Cold start time is greater than 5000 ms for the last 6 hours.                     |
| **High Frozen Frames**   | The UI freezes more than 10 times in the last 6 hours.                            |
| **Low Frame Rate**       | More than 10 slow frames occur in the last 6 hours. A slow frame is below 60 fps. |
| **High Network Latency** | API response time exceeds 2000 ms for the last 6 hours.                           |

## Next steps

- Explore your data in [Frontend Observability](/docs/grafana-cloud/observe-and-act/monitor-applications/frontend-observability/)
