Flutter
The Middleware Flutter RUM SDK collects user activity, performance data, and errors from Flutter applications. It helps you investigate navigation problems, rendering delays, crashes, and slow or failed requests.
Session data and supported recordings provide context for application problems. Custom events and spans can describe operations such as login, checkout, or data synchronization, making it easier to identify which part of a workflow failed or took longer than expected.
Prerequisites#
Before installing the SDK, make sure you have:
- Flutter 3.29.0 or later.
- Dart 3.7.0 or later.
- A Middleware account and RUM application credentials.
- Access to your Flutter application source code.
- HTTPS connectivity to the Middleware ingestion endpoint.
For mobile applications, additional requirements apply:
| Platform | Requirements |
|---|---|
| Android | compileSdk 35+, Kotlin Gradle Plugin 2.0+, core library desugaring |
| iOS | iOS 13.0+, CocoaPods support |
The Flutter SDK includes native Middleware SDKs for Android and iOS to support crash reporting, ANR detection, and native Session Replay.
Installation#
1 Create a RUM application#
- Log in to Middleware.
- Go to Installation → RUM.
- Select Flutter as the platform.
- Click New Application.
- Enter your application name and click Create Application.
- Copy the generated account key and target URL.
2 Install the Flutter SDK#
Add the Middleware SDK to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
middleware_flutter_opentelemetry: ^2.2.3Install the dependencies:
flutter pub getFor iOS, add the Middleware specs repo and the CocoaPods CDN to the top of ios/Podfile:
source 'https://github.com/middleware-labs/cocoapods-specs.git'
source 'https://cdn.cocoapods.org/'Keep both sources: new MiddlewareRum versions are published to the Middleware repo, and the SDK's dependencies still come from the CocoaPods CDN. CocoaPods trunk stops accepting new pods on December 2, 2026, so without the Middleware source your app stays on the last version published to trunk.
Then install the native pods:
cd ios
pod install
cd ..Note: If you're upgrading from an earlier SDK version, run pod update MiddlewareRum in ios/ instead of pod install. The SDK needs MiddlewareRum 2.3.1 or later, and pod install fails when Podfile.lock pins an older version.
3 Initialize Middleware#
Initialize the SDK in main.dart before calling runApp().
import 'package:flutter/material.dart';
import 'package:middleware_flutter_opentelemetry/middleware_flutter_opentelemetry.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterOTel.initialize(
serviceName: 'my-flutter-app',
serviceVersion: '1.0.0',
tracerName: 'main',
middlewareAccountKey: '<MW_ACCOUNT_KEY>',
endpoint: 'https://<MW_UID>.middleware.io',
resourceAttributes: OTel.attributesFromMap({
'env': 'production',
'service.namespace': 'mobile-apps',
}),
);
runApp(const MyApp());
}Replace the placeholders with your Middleware credentials.
| Parameter | Required | Description |
|---|---|---|
serviceName | Yes | Name used to identify your Flutter application |
serviceVersion | No | Application version |
tracerName | No | Name of the OpenTelemetry tracer |
middlewareAccountKey | Yes | Middleware account key |
endpoint | Yes | Middleware ingestion endpoint |
resourceAttributes | No | Additional attributes such as environment or namespace |
Initialize Middleware as early as possible to capture startup activity, navigation, and errors.
Flutter and platform errors are captured automatically (autoCaptureErrors, on by default). Error handlers you set before initialize() keep running. To report errors you catch yourself, call FlutterOTel.reportError().
To track navigation, add the route observer. For GoRouter:
final router = GoRouter(
observers: [FlutterOTel.routeObserver],
routes: [ /* ... */ ],
);For Navigator:
MaterialApp(
navigatorObservers: [FlutterOTel.routeObserver],
// ...
);4 Run and verify#
Run your Flutter application:
flutter runTo verify data collection:
- Open the application on an emulator or physical device.
- Navigate between screens and interact with the application.
- Go to
RUM → Session Explorerin Middleware. - Verify that new sessions and navigation events appear.
Read more about RUM explorer and see what data to monitor for frontend performance.
Optional configuration#
Configure Session Replay#
Session Replay is supported on Android, iOS, and Web, with platform-specific recording behavior.
Android and iOS#
On Android and iOS, Session Replay uses the embedded native Middleware SDKs.
Recording starts automatically when Middleware initializes.
No additional RepaintBoundary, startSessionRecording(), or stopSessionRecording() calls are required.
Flutter Web#
For Flutter Web, the SDK uses a Dart-based recorder. It starts by itself after the first frame and captures the whole app, so no widget wrapping is needed.
To record only part of the UI, wrap it in a RepaintBoundary with the SDK key:
RepaintBoundary(
key: FlutterOTel.repaintBoundaryKey,
child: const MyApp(),
)Turn recording off or on#
To disable Session Replay:
await FlutterOTel.initialize(
serviceName: 'my-flutter-app',
middlewareAccountKey: '<MW_ACCOUNT_KEY>',
endpoint: 'https://<MW_UID>.middleware.io',
enableSessionRecording: false,
);To pause and resume recording at runtime:
await FlutterOTel.stopSessionRecording();
await FlutterOTel.startSessionRecording();On Android and iOS, both calls stay in effect across new sessions until you call the other one, and startSessionRecording() also overrides enableSessionRecording: false. You can use this to record only a specific flow, such as checkout. On Flutter Web, enableSessionRecording: false turns the recorder off completely.
Session limits#
- Maximum session duration: 4 hours.
- Idle timeout: 15 minutes.
A new session begins after the inactivity timeout.
Privacy recommendation: Review sensitive screens and user inputs before enabling Session Replay in production. Avoid including passwords, tokens, payment details, or personal information in custom telemetry attributes.
Monitor HTTP requests#
The SDK records HTTP requests automatically, with request duration, response status, and errors.
- Android and iOS: every request made through
dart:io, includingpackage:httpanddio, gets an HTTP span and trace headers. No client wrapping is needed. Clients created beforeFlutterOTel.initialize()aren't covered. - Flutter Web: the browser instrumentation records
fetchandXMLHttpRequestrequests.
To turn off automatic HTTP capture on Android and iOS, set enableAutomaticNetworkInstrumentation: false.
Configure distributed tracing#
Distributed tracing connects Flutter HTTP requests with backend APM traces.
On Android and iOS, the SDK adds W3C Trace Context (traceparent) and B3 (b3, x-b3-*) headers to traced requests. On Flutter Web, trace headers are added to same-origin requests; add other backends with webInstrumentation: WebInstrumentationOptions(tracePropagationTargets: [...]).
Prerequisites#
- Your backend must have Middleware APM instrumentation enabled.
- The backend must support W3C Trace Context or B3.
- On Flutter Web, cross-origin backends must allow the trace headers in their CORS policy.
Verify trace correlation#
- Open the Flutter application.
- Trigger a request to an instrumented backend service.
- Go to RUM → Session Explorer.
- Open the corresponding session and network event.
- Follow the associated backend trace when available.
If backend spans are missing, verify that tracing headers reach the backend and that its APM instrumentation is configured correctly.
Track user interactions#
Use widget extensions to record application-specific interactions.
Track button clicks#
ElevatedButton(
onPressed: handleSubmit,
child: const Text('Submit'),
).withOTelButtonTracking('submit_form');Track text field interactions#
TextField(
decoration: const InputDecoration(
labelText: 'Enter something',
),
).withOTelTextFieldTracking('demo_text_field');Add an error boundary#
RiskyWidget().withOTelErrorBoundary('risky_operation');These events provide additional context when investigating what happened before an error.
Avoid collecting sensitive text field values.
Add custom spans#
Use custom spans to track important application workflows, such as login, checkout, or data synchronization.
SpanStatusCode comes from middleware_dart_opentelemetry, which the Flutter SDK already uses. Add it as a direct dependency so you can import it:
flutter pub add middleware_dart_opentelemetryimport 'package:middleware_dart_opentelemetry/middleware_dart_opentelemetry.dart'
show SpanStatusCode;
import 'package:middleware_flutter_opentelemetry/middleware_flutter_opentelemetry.dart';
final tracer = FlutterOTel.tracer;
final span = tracer.startSpan(
'checkout',
attributes: OTel.attributesFromMap({
'operation': 'payment',
'screen': 'checkout',
}),
);
try {
await processPayment();
span.setStatus(SpanStatusCode.Ok);
} catch (error, stackTrace) {
span.recordException(error, stackTrace: stackTrace);
span.setStatus(SpanStatusCode.Error, error.toString());
rethrow;
} finally {
span.end();
}Custom spans help identify which application operation failed or took longer than expected.
Configure environment-specific settings#
Use Flutter's --dart-define arguments to pass different settings for development, staging, and production.
For example:
flutter run \
--dart-define=OTEL_SERVICE_NAME=my-flutter-app \
--dart-define=OTEL_SERVICE_VERSION=1.0.0 \
--dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector:4317 \
--dart-define=OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
--dart-define=OTEL_EXPORTER_OTLP_HEADERS=Authorization=your-api-keyThese values can also be used in CI/CD pipelines to configure different environments.
Important: --dart-define values are embedded in the application build and should not be treated as secrets. Use only credentials intended for client-side SDKs.
Configuration reference#
Common parameters accepted by FlutterOTel.initialize():
| Parameter | Description |
|---|---|
serviceName | Application service name |
serviceVersion | Application version |
tracerName | OpenTelemetry tracer name |
middlewareAccountKey | Middleware account key |
endpoint | Middleware ingestion endpoint |
resourceAttributes | Custom resource attributes |
autoCaptureErrors | Registers Flutter and platform error handlers. On by default |
enableSessionRecording | Enables or disables Session Replay. On by default |
enableAutomaticNetworkInstrumentation | Records HTTP requests on Android and iOS. On by default |
Troubleshooting#
No RUM data appears#
If the application runs but telemetry does not appear:
- Verify that the Middleware package is installed.
- Confirm that
FlutterOTel.initialize()runs beforerunApp(). - Check the account key and ingestion endpoint.
- Verify that the device can connect to Middleware.
- Review the Flutter console for SDK initialization or export errors.
Flutter errors are not captured#
- Confirm that
autoCaptureErrorsis not set tofalse. - If you set
FlutterError.onErrororPlatformDispatcher.instance.onErrorafterinitialize(), it replaces the SDK handler. Set your handlers beforeinitialize(), or callFlutterOTel.reportError()from them. - Report errors you catch yourself through
FlutterOTel.reportError().
HTTP requests are missing#
- Confirm that
enableAutomaticNetworkInstrumentationis not set tofalse. - Create HTTP clients after
FlutterOTel.initialize(). Clients created earlier aren't traced. - Check whether your networking library uses its own HTTP stack instead of
dart:io, such as a native platform client. - Review exporter logs for failed telemetry uploads.
Backend traces are not correlated#
- Verify that backend APM instrumentation is enabled.
- Confirm that the backend supports W3C Trace Context.
- Check that
traceparentheaders are propagated with instrumented requests. - Ensure that gateways or proxies do not remove tracing headers.
Session Replay is missing#
For Android and iOS:#
- Confirm that
enableSessionRecordingis not set tofalse. - Verify that the native Middleware SDK initializes successfully.
- Check that the application has generated user activity.
For Flutter Web:#
- Confirm that
enableSessionRecordingis not set tofalse. - Check that your code doesn't call
stopSessionRecording()before the session ends. - If you wrap part of the UI with
FlutterOTel.repaintBoundaryKey, only that part is recorded.
Android build errors#
- Verify that
compileSdkis 35 or later. - Confirm that Kotlin Gradle Plugin 2.0+ is configured.
- Enable core library desugaring.
- Run
flutter cleanand reinstall dependencies if required.
iOS build errors#
- Verify that the deployment target is iOS 13 or later.
- Run
cd ios && pod install. - If CocoaPods can't find or upgrade
MiddlewareRum, check both sources are in your Podfile and runpod update MiddlewareRum. - Check CocoaPods static framework linking.
- Rebuild the iOS project after installing the SDK.
If the issue persists, contact Middleware Support at support@middleware.io.
Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us or join our Slack channel.
What did you think of this content?