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)
- Android:
CDN artifacts
| Artifact | URL |
|---|---|
| Tarball | https://cdn.last9.io/rum-sdk/flutter/builds/0.3.1/last9_rum_flutter-0.3.1.tar.gz |
| Checksum | https://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
-
Download, verify, and extract the SDK
VERSION=0.3.1curl -L -o last9_rum_flutter.tar.gz \"https://cdn.last9.io/rum-sdk/flutter/builds/${VERSION}/last9_rum_flutter-${VERSION}.tar.gz"# Verify checksumEXPECTED=$(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 vendortar xzf last9_rum_flutter.tar.gz -C vendor/rm last9_rum_flutter.tar.gz -
Add as a path dependency
In
pubspec.yaml:dependencies:last9_rum_flutter:path: vendor/flutterThen:
flutter pub get -
Android — add the CDN Maven repository
dependencyResolutionManagement {repositories {google()mavenCentral()maven { url = uri("https://cdn.last9.io/rum-sdk/android/maven/") }}}dependencyResolutionManagement {repositories {google()mavenCentral()maven { url = uri("https://cdn.last9.io/rum-sdk/android/maven/") }}} -
iOS — add the Last9RUM podspec
In
ios/Podfile, inside thetarget 'Runner'block:target 'Runner' douse_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__))endThen:
cd ios && pod install -
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',});
// Clearawait 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.