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:

PlatformRequirements
AndroidcompileSdk 35+, Kotlin Gradle Plugin 2.0+, core library desugaring
iOSiOS 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#

  1. Log in to Middleware.
  2. Go to Installation → RUM.
  3. Select Flutter as the platform.
  4. Click New Application.
  5. Enter your application name and click Create Application.
  6. Copy the generated account key and target URL.

2 Install the Flutter SDK#

Add the Middleware SDK to your pubspec.yaml:

yml
dependencies:
  flutter:
    sdk: flutter
  middleware_flutter_opentelemetry: ^2.2.3

Install the dependencies:

bash
flutter pub get

For iOS, add the Middleware specs repo and the CocoaPods CDN to the top of ios/Podfile:

ruby
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:

bash
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().

js
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.

ParameterRequiredDescription
serviceNameYesName used to identify your Flutter application
serviceVersionNoApplication version
tracerNameNoName of the OpenTelemetry tracer
middlewareAccountKeyYesMiddleware account key
endpointYesMiddleware ingestion endpoint
resourceAttributesNoAdditional 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:

js
final router = GoRouter(
  observers: [FlutterOTel.routeObserver],
  routes: [ /* ... */ ],
);

For Navigator:

js
MaterialApp(
  navigatorObservers: [FlutterOTel.routeObserver],
  // ...
);

4 Run and verify#

Run your Flutter application:

bash
flutter run

To verify data collection:

  1. Open the application on an emulator or physical device.
  2. Navigate between screens and interact with the application.
  3. Go to RUM → Session Explorer in Middleware.
  4. 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:

js
RepaintBoundary(
  key: FlutterOTel.repaintBoundaryKey,
  child: const MyApp(),
)

Turn recording off or on#

To disable Session Replay:

js
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:

js
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, including package:http and dio, gets an HTTP span and trace headers. No client wrapping is needed. Clients created before FlutterOTel.initialize() aren't covered.
  • Flutter Web: the browser instrumentation records fetch and XMLHttpRequest requests.

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#

  1. Open the Flutter application.
  2. Trigger a request to an instrumented backend service.
  3. Go to RUM → Session Explorer.
  4. Open the corresponding session and network event.
  5. 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#

js
ElevatedButton(
  onPressed: handleSubmit,
  child: const Text('Submit'),
).withOTelButtonTracking('submit_form');

Track text field interactions#

js
TextField(
  decoration: const InputDecoration(
    labelText: 'Enter something',
  ),
).withOTelTextFieldTracking('demo_text_field');

Add an error boundary#

js
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:

bash
flutter pub add middleware_dart_opentelemetry
js
import '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:

bash
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-key

These 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():

ParameterDescription
serviceNameApplication service name
serviceVersionApplication version
tracerNameOpenTelemetry tracer name
middlewareAccountKeyMiddleware account key
endpointMiddleware ingestion endpoint
resourceAttributesCustom resource attributes
autoCaptureErrorsRegisters Flutter and platform error handlers. On by default
enableSessionRecordingEnables or disables Session Replay. On by default
enableAutomaticNetworkInstrumentationRecords 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 before runApp().
  • 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 autoCaptureErrors is not set to false.
  • If you set FlutterError.onError or PlatformDispatcher.instance.onError after initialize(), it replaces the SDK handler. Set your handlers before initialize(), or call FlutterOTel.reportError() from them.
  • Report errors you catch yourself through FlutterOTel.reportError().

HTTP requests are missing#

  • Confirm that enableAutomaticNetworkInstrumentation is not set to false.
  • 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 traceparent headers 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 enableSessionRecording is not set to false.
  • Verify that the native Middleware SDK initializes successfully.
  • Check that the application has generated user activity.

For Flutter Web:#

  • Confirm that enableSessionRecording is not set to false.
  • 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 compileSdk is 35 or later.
  • Confirm that Kotlin Gradle Plugin 2.0+ is configured.
  • Enable core library desugaring.
  • Run flutter clean and 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 run pod 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?