Skip to content
Last9
Book demo

Flutter RUM SDK

Install and configure the Last9 Flutter RUM SDK. Dart plugin over native Android and iOS SDKs, CDN-hosted tar.gz, auto-instruments HttpClient, NavigatorObserver, errors, ANRs.

Real User Monitoring for Flutter apps. Automatic instrumentation for sessions, views, network requests, errors, and resource metrics via OpenTelemetry. ANR detection is available on Android only.

The Flutter SDK is a Dart plugin over the Android and iOS native SDKs. Both platform CDN repos must be configured so native dependencies resolve.

Prerequisites

  • Flutter >= 3.10.0
  • Dart SDK >= 3.0.0
  • iOS 15.1+
  • Android minSdk 21 (Android 5.0+)
  • Native dependencies:
    • Android: io.last9:rum-android:0.3.1 (resolved from CDN Maven)
    • iOS: Last9RUM 0.3.1 (resolved from CDN podspec)

CDN artifacts

ArtifactURL
Tarballhttps://cdn.last9.io/rum-sdk/flutter/builds/0.3.1/last9_rum_flutter-0.3.1.tar.gz
Checksumhttps://cdn.last9.io/rum-sdk/flutter/builds/0.3.1/last9_rum_flutter-0.3.1.tar.gz.sha256

Staging builds use the -alpha.<run_number> suffix.

Installation

  1. Download, verify, and extract the SDK

    VERSION=0.3.1
    curl -L -o last9_rum_flutter.tar.gz \
    "https://cdn.last9.io/rum-sdk/flutter/builds/${VERSION}/last9_rum_flutter-${VERSION}.tar.gz"
    # Verify checksum
    EXPECTED=$(curl -sL "https://cdn.last9.io/rum-sdk/flutter/builds/${VERSION}/last9_rum_flutter-${VERSION}.tar.gz.sha256")
    ACTUAL=$(shasum -a 256 last9_rum_flutter.tar.gz | awk '{print $1}')
    [ "$EXPECTED" = "$ACTUAL" ] && echo "OK" || echo "CHECKSUM MISMATCH"
    # Extract into vendor/
    mkdir -p vendor
    tar xzf last9_rum_flutter.tar.gz -C vendor/
    rm last9_rum_flutter.tar.gz
  2. Add as a path dependency

    In pubspec.yaml:

    dependencies:
    last9_rum_flutter:
    path: vendor/flutter

    Then:

    flutter pub get
  3. Android — add the CDN Maven repository

    dependencyResolutionManagement {
    repositories {
    google()
    mavenCentral()
    maven { url = uri("https://cdn.last9.io/rum-sdk/android/maven/") }
    }
    }
  4. iOS — add the Last9RUM podspec

    In ios/Podfile, inside the target 'Runner' block:

    target 'Runner' do
    use_frameworks!
    pod 'Last9RUM', :podspec => 'https://cdn.last9.io/rum-sdk/ios/builds/0.3.1/Last9RUM.podspec'
    flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
    end

    Then:

    cd ios && pod install
  5. Initialize the SDK

    import 'package:flutter/widgets.dart';
    import 'package:last9_rum_flutter/last9_rum_flutter.dart';
    void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await L9Rum.initialize(const L9RumConfig(
    baseUrl: 'https://otlp-ext-aps1.last9.io/v1/otlp/organizations/<org>',
    origin: 'https://app.last9.io',
    clientToken: 'your-client-token',
    serviceName: 'my-flutter-app',
    serviceVersion: '1.0.0',
    deploymentEnvironment: 'production',
    ));
    runApp(const MyApp());
    }

Configuration

const L9RumConfig config = L9RumConfig(
// --- Required ---------------------------------------------------------
baseUrl: 'https://otlp-ext-aps1.last9.io/v1/otlp/organizations/<org>',
clientToken: 'your-client-token',
serviceName: 'my-flutter-app',
serviceVersion: '1.0.0',
deploymentEnvironment: 'production',
// --- Optional ---------------------------------------------------------
// Origin sent as X-LAST9-ORIGIN header.
// Required for client_monitoring tokens.
origin: 'https://app.last9.io',
// Specific build identifier (maps to app.build_id)
appBuildId: '1.0.0-build-42',
// Unique per-install ID (NOT a hardware ID). Persists across launches.
appInstallationId: null,
// Session sampling rate: 0-100 (percentage). 100 = sample everything.
sampleRate: 100,
// Print debug logs to native console
debugLogs: false,
// Automatically intercept HTTP requests via dart:io HttpOverrides
networkInstrumentation: true,
// Automatically capture unhandled Dart errors
errorInstrumentation: true,
// Max spans per export batch
maxExportBatchSize: 100,
// Export timeout in milliseconds
exportTimeoutMs: 30000,
// ANR detection (Android only)
anrDetectionEnabled: true,
anrThresholdMs: 5000,
// Periodically sample memory and CPU
resourceMonitoringEnabled: true,
resourceSamplingIntervalMs: 30000,
// When true, each HTTP request gets its own traceId
// instead of sharing the current view's traceId
isolateTracePerRequest: false,
// Custom resource attributes added to every span
resourceAttributes: <String, String>{
'app.platform': 'flutter',
},
// W3C Baggage propagation on outgoing requests
baggage: L9BaggageConfig(
enabled: false,
allowedKeys: <String>['session.id', 'user.id'],
maxTotalBytes: 8192,
warnAtPercentage: 80,
),
);

API reference

Automatic view tracking

Attach L9NavigationObserver to your MaterialApp or CupertinoApp:

MaterialApp(
navigatorObservers: <NavigatorObserver>[L9NavigationObserver()],
// ...
);

HTTP client instrumentation

When networkInstrumentation is true, the SDK installs a dart:io HttpOverrides that wraps the default HttpClient. Requests made through the http package and dio go through HttpClient under the hood and are traced automatically — no extra setup needed.

Identify a user

await L9Rum.identify(const L9UserInfo(
id: 'user-123',
name: 'Jane',
email: 'jane@example.com',
fullName: 'Jane Doe',
roles: <String>['admin'],
));

Clear user on sign-out

await L9Rum.clearUser();

Capture errors

try {
// risky operation
} catch (error, stackTrace) {
await L9Rum.captureError(
error,
stackTrace: stackTrace,
context: <String, dynamic>{'screen': 'checkout'},
);
}

Unhandled error forwarding

Flutter’s error surfaces are separate — framework, platform dispatcher, and async zones each need wiring:

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await L9Rum.initialize(config);
// Flutter framework errors
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
L9Rum.captureError(details.exception, stackTrace: details.stack);
};
// Platform dispatcher errors
PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
L9Rum.captureError(error, stackTrace: stack);
return true;
};
// Async errors via zone
runZonedGuarded(
() => runApp(const MyApp()),
(Object error, StackTrace stack) {
L9Rum.captureError(error, stackTrace: stack);
},
);
}

Track views manually

await L9Rum.startView('ProductDetailsScreen');
await L9Rum.setViewName('Product #42');

Custom events

await L9Rum.addEvent('purchase_completed', attributes: <String, dynamic>{
'product_id': '12345',
'amount': 29.99,
});

Global span attributes

await L9Rum.spanAttributes(<String, dynamic>{
'experiment': 'checkout_v2',
'feature_flag': 'new_cart',
});
// Clear
await L9Rum.spanAttributes(null);

Session ID

final String? sessionId = await L9Rum.getSessionId();

Flush pending data

await L9Rum.flush();

Next steps

Once data is flowing, explore it in Discover > Applications — performance, errors, and sessions.


Troubleshooting

Please get in touch with us on Discord or Email if you have any questions.