Note
Observability for Mobile is currently in private preview. Grafana Labs offers support on a best-effort basis, and breaking changes might occur prior to the feature being made generally available.
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.
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.
Before you begin
Confirm the following before you instrument the application:
- A Grafana Cloud account (sign up for free)
- 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:
- Sign in to your Grafana Cloud instance.
- Click Observability > Frontend.
- Click Create New > Mobile Application.
- Enter an Application name.
- 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:
dependencies:
faro: ^0.17.0Install the package:
flutter pub getInitialize 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.
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:
import 'dart:io';Add this line as the first line inside main(), before Faro().runApp():
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:
return MaterialApp(
navigatorObservers: [FaroNavigationObserver()],
home: myExistingHome,
);Each screen change sends a view_changed event. For GoRouter, set observers instead of navigatorObservers:
GoRouter(
observers: [FaroNavigationObserver()],
);To track taps and gestures, wrap your existing widget inside appRunner. If that call is runApp(const MyApp()), replace it with:
runApp(
const FaroUserInteractionWidget(child: MyApp()),
);To also track asset loading, use this runApp call instead of the previous one:
runApp(
FaroAssetTracking(
child: const FaroUserInteractionWidget(child: MyApp()),
),
);To keep telemetry while the device is offline, add this immediately before Faro().runApp():
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.
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.
Faro().pushEvent('checkout-button-tapped', attributes: {
'startedInBackgroundCount': '1',
'from': 'notification',
});Send a log with a severity:
Faro().pushLog('My warn message', level: LogLevel.warn);Send a measurement when the value is a number:
Faro().pushMeasurement({'cpu_usage': 350}, 'app_cpu_usage');Report an error you have already caught. stackTrace comes from the catch clause:
} 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.
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.
Next steps
- Explore your data in Frontend Observability


