Flutter SDK
Track errors, breadcrumbs, logs, performance, and native crashes in Flutter apps across Web, Android, iOS, Desktop, and Chrome Extensions.
Initialize with just a few lines of code
Works on Web, Android, iOS, and Chrome Extensions
Catches uncaught errors automatically
Capture custom errors and messages
Automatically detects the running platform
Flexible configuration options
Add custom data, tags, and user context to errors
Track user actions and events before errors
Structured log ingestion with batching and levels
Track HTTP requests and custom operations
Captures native crashes on iOS and Android and reports them on next app launch
Using an AI assistant? Use Copy page for AI at the top of this page and paste it into the chat. See AI assistants.
Supported Platforms#
- Flutter Web — Full support with JS error catching
- Chrome Extension — Optimized for extension environment
- Android — Dart errors plus native crash capture (bundled native Abrova Trace SDK)
- iOS — Dart errors plus native crash capture (bundled native Abrova Trace SDK)
- Desktop — Windows, macOS, Linux (Dart errors only; no native crash capture)
Installation#
The package comes from Abrova Center, so you do not need access to any other package registry. Two steps: point your project at the Abrova registry, then install.
1. Add the package from the Abrova registry in pubspec.yaml:
# pubspec.yaml
dependencies:
abrova_trace_flutter_sdk:
hosted: https://center.abrova.ir/api/packages/abrova/pub
version: ^0.2.62. Add the native SDKs' sources. The plugin uses the native Android and iOS SDKs for crash capture.
Android: add the Abrova Maven repository in android/build.gradle (or android/settings.gradle, wherever your project lists repositories):
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://center.abrova.ir/api/packages/abrova/maven" }
}
}iOS: add this inside the app target in ios/Podfile:
pod 'AbrovaTrace', :git => 'https://center.abrova.ir/abrova/abrova-trace-ios-sdk.git', :tag => '0.2.4'
Then install:
flutter pub get
Requirements: Dart SDK 3.8.1 or newer. Android: API 19 (Android 4.4) or newer. iOS: 12.0 or newer, built with Xcode 15.3+ (Swift 5.10). On web, Dart errors, messages, logs and performance work; native crash capture is Android and iOS only.
Use your project's public key ab_live_… from Abrova Console. It is safe inside your app. Never put the server secret ab_srv_… in an app.
Quick Start#
Basic Initialization
import 'package:flutter/material.dart';
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize AbrovaTrace
await AbrovaTrace.init(AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
release: '1.0.0',
));
runApp(MyApp());
}Recommended: Initialize with Zone (Better Error Catching)
Using initWithZone catches async errors that would otherwise be missed. This is the recommended approach for production apps.
import 'package:flutter/material.dart';
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';
void main() async {
// IMPORTANT: Initialize AbrovaTrace FIRST, before WidgetsFlutterBinding
await AbrovaTrace.initWithZone(
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
release: '1.0.0',
captureUncaughtErrors: true,
),
() async {
// Move all initialization inside this callback
WidgetsFlutterBinding.ensureInitialized();
// Any other async initialization goes here
// await Firebase.initializeApp();
// await setupDatabase();
runApp(MyApp());
},
);
}Capture Errors#
Capture Exceptions
try {
riskyOperation();
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace,
message: 'Failed to perform risky operation',
extra: {'user_id': 'user123'},
);
}Capture Messages
await AbrovaTrace.captureMessage(
'User completed onboarding',
extra: {'step': 'welcome_screen'},
);
// With a level and tags
await AbrovaTrace.captureMessage(
'API rate limit approaching',
level: MessageLevel.warning,
tags: {'feature': 'sync'},
);Messages appear in the Console under Abrova Trace → Messages, with their level, platform, extra data and breadcrumbs. They never create an issue. Levels: MessageLevel.debug, info (default), warning, error. Use messages for things your app reports on purpose ("checkout started", "sync finished"); for high-volume diagnostics use Logs.
Custom Error Context
await AbrovaTrace.captureException(
exception,
extra: {
'user_id': 'user123',
'feature': 'shopping_cart',
'action': 'checkout',
'cart_items': 5,
'total_amount': 99.99,
},
);User Context#
Associate errors and logs with specific users and add custom metadata. If you use Abrova Auth, pass the signed-in user's id.
Set User ID
// Set after user login
AbrovaTrace.setUserId('user_123');
// Get current user ID
final userId = AbrovaTrace.getUserId();Custom Extra Data
// Set individual extra
AbrovaTrace.setExtra('subscription', 'premium');
// Set multiple extras at once
AbrovaTrace.setExtras({
'subscription': 'premium',
'feature_flags': 'dark_mode,beta',
});
// Read extra data
final sub = AbrovaTrace.getExtra('subscription');
final allExtras = AbrovaTrace.getExtras();
// Clear all extras
AbrovaTrace.clearExtras();Tags
// Set individual tag
AbrovaTrace.setTag('team', 'checkout');
// Set multiple tags at once
AbrovaTrace.setTags({
'team': 'checkout',
'module': 'payments',
});
// Read tags
final team = AbrovaTrace.getTag('team');
final allTags = AbrovaTrace.getTags();
// Clear all tags
AbrovaTrace.clearTags();Breadcrumbs#
Breadcrumbs track user actions and events leading up to an error, helping you understand what happened before the crash.
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';
// User action breadcrumb
AbrovaTrace.addUserBreadcrumb('Added item to cart', data: {
'product_id': 'abc',
'quantity': 2,
});
// Navigation breadcrumb
AbrovaTrace.addNavigationBreadcrumb('HomeScreen', 'ProductDetail');
// HTTP request breadcrumb
AbrovaTrace.addHttpBreadcrumb('POST', 'https://api.example.com/orders', statusCode: 201);
// Generic breadcrumb
AbrovaTrace.addBreadcrumb(
'Order placed',
type: BreadcrumbType.info,
data: {'order_id': '12345', 'total': 99.99},
);
// Clear all breadcrumbs
AbrovaTrace.clearBreadcrumbs();Auto-Capture Taps (Gesture Breadcrumbs)#
Automatically log every user tap as a breadcrumb. Works on every Flutter target — Web, Android, iOS, macOS, Windows, Linux — because it hooks into the framework's pointer router, not platform code.
When an error fires, the issue detail shows the widget the user tapped just before — usually enough to reproduce a bug without a perfect stack trace. This is especially useful on Flutter Web, where production stack traces can be partially symbolicated due to dart2js inlining.
Enable
Call once after init, after WidgetsFlutterBinding.ensureInitialized(). Idempotent — safe to call multiple times.
await AbrovaTrace.initWithZone(
AbrovaTraceConfig(apiKey: 'ab_live_...'),
() async {
WidgetsFlutterBinding.ensureInitialized();
// Enable automatic tap breadcrumbs
AbrovaTrace.enableGestureBreadcrumbs();
runApp(MyApp());
},
);What gets captured
Every tap on a recognized interactive widget produces a breadcrumb like:
{
"message": "Tap on ElevatedButton: \"Pay\"",
"type": "user",
"timestamp": "2026-05-15T14:32:18.421Z",
"data": {
"action": "tap",
"widget": "ElevatedButton",
"label": "Pay",
"key": "checkout-pay-button",
"path": "CheckoutPage > Scaffold",
"route": "/checkout",
"position": { "x": 187, "y": 614 }
}
}The route field is filled when you add AbrovaTraceNavigatorObserver() to your app's navigatorObservers (it also records navigation breadcrumbs automatically).
Recognized widgets
The tracker classifies these widget types out of the box (more specific types take priority):
- ElevatedButton
- TextButton
- OutlinedButton
- FilledButton
- IconButton
- FloatingActionButton
- Switch
- Checkbox
- Radio
- Slider
- PopupMenuButton
- DropdownButton
- InkWell
- InkResponse
- GestureDetector
- CupertinoButton
Tip: Add ValueKey to important widgets
The breadcrumb tries to find a label in this order: Text child → Semantics.label → Tooltip.message → Icon.semanticLabel.
For widgets without text (e.g. IconButton), add a ValueKey so you can grep for it in your code:
IconButton(
key: const ValueKey('cart-remove-row-2'), // ← surfaces in breadcrumb
icon: const Icon(Icons.delete),
onPressed: _removeItem,
)Disable
AbrovaTrace.disableGestureBreadcrumbs();
The tracker captures the visible widget label — typically a button caption like "Pay" or "Add to Cart". It does not capture form input values, text-field contents, or anything entered by the user. A ValueKey is reported in addition to the label, not instead of it, so keep sensitive text out of the labels of tappable widgets.
Logging#
Structured log ingestion with automatic batching and mobile lifecycle awareness. Enable it once, then use the level helpers from anywhere in your app:
// Enable logging after AbrovaTrace.init()
AbrovaTrace.enableLogging(sourceId: 'my-flutter-app');
AbrovaTrace.info('User login successful', metadata: {'user_id': 'user_123'});See Flutter SDK Logging for the full pipeline — batching, log levels, background flushing, manual flush, and configuration.
Performance Tracking#
Track HTTP request latency and custom operations for APM.
Automatic HTTP Client
// Create an HTTP client that automatically tracks all requests
final client = AbrovaTrace.createPerformanceClient();
final response = await client.get(Uri.parse('https://api.example.com/users'));
// Duration, status code, and errors are automatically reportedcreatePerformanceClient() takes the same options as PerformanceHttpClient (below) and fills in your API key, API URL, environment, release and platform from the config: inner, addBreadcrumbs (default true), excludedHosts, minDurationMs (default 0).
PerformanceHttpClient
PerformanceHttpClient is an http.BaseClient (from package:http) that times every request and reports it as an http span. Use it directly when you want to wrap an existing client or construct it yourself:
import 'package:http/http.dart' as http;
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';
final client = PerformanceHttpClient(
apiKey: 'ab_live_...', // Required
environment: 'production',
releaseVersion: '1.0.0',
inner: http.Client(), // Default: a new http.Client()
addBreadcrumbs: true, // Also add HTTP breadcrumbs (default: true)
excludedHosts: {'analytics.example.com'}, // Hosts to skip
minDurationMs: 50, // Skip requests faster than this (default: 0)
);
final response = await client.get(Uri.parse('https://api.example.com/users'));
// Or wrap an existing client
final tracked = http.Client().withPerformanceTracking(apiKey: 'ab_live_...');
// Send buffered spans now, and stop the client when done
await client.flush();
client.close();Spans are buffered and sent in batches of 10, or every 10 seconds. Requests to Abrova hosts are never tracked. Breadcrumb URLs have sensitive query parameters (token, password, api_key, ...) replaced with [REDACTED]. Spans show up under Abrova Trace → Performance.
Track Custom Operations
final result = await AbrovaTrace.trackOperation(
'load_user_profile',
() async {
final user = await fetchUser();
await cacheUser(user);
return user;
},
operationType: 'custom',
);Manual Span Reporting
final start = DateTime.now();
// Report a single performance span
await AbrovaTrace.reportPerformanceSpan(
PerformanceSpan.fromHttpRequest(
method: 'GET',
url: 'https://api.example.com/products',
statusCode: 200,
startTime: start,
durationMs: 245,
),
);
// Report a batch of spans
await AbrovaTrace.reportPerformanceSpanBatch([
PerformanceSpan.fromHttpRequest(
method: 'GET',
url: '/api/users',
statusCode: 200,
startTime: start,
durationMs: 150,
),
PerformanceSpan.custom(
operation: 'image_processing',
startTime: start,
durationMs: 800,
),
]);Platform-Specific Usage#
Chrome Extension
void main() async {
await AbrovaTrace.init(AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
debug: false, // Disable in production extensions
));
runApp(MyExtensionApp());
}Mobile App
void main() async {
// Required before init() on Android and iOS
WidgetsFlutterBinding.ensureInitialized();
await AbrovaTrace.init(AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
captureUncaughtErrors: true, // Recommended for mobile
));
runApp(MyMobileApp());
}Web App
void main() async {
await AbrovaTrace.init(AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
debug: kDebugMode, // Enable debug in development
));
runApp(MyWebApp());
}Crash Detection#
AbrovaTrace provides comprehensive error detection for any Flutter app, regardless of what libraries or native code you use.
- Dart Exceptions — Captured in real-time
- Async Errors — Captured in real-time (with initWithZone)
- Platform Exceptions — Captured in real-time
- Native Crashes — iOS and Android only; reported on next app launch
How Native Crash Detection Works
The plugin bundles the native Abrova Trace SDKs for iOS and Android and starts them during init() / initWithZone():
- iOS — PLCrashReporter records signal and Mach exception crashes to disk
- Android — an uncaught exception handler, plus
ApplicationExitInfo(Android 11 / API 30+) for native crashes and ANRs - On the next launch, the native SDK reads the saved report and sends it to Abrova Trace as an issue
No extra code is needed. Crashes are sent on the next launch, so a crash appears after the user reopens the app.
AbrovaTrace.close() flushes pending logs and releases the SDK. It is not needed for crash detection. After close(), calls such as captureException() throw until you call init() again, so only call it when your app is really shutting down.
Configuration Options#
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key', // Required
environment: 'production', // Default: 'production'
release: '1.0.0', // Optional
captureUncaughtErrors: true, // Default: true
enabled: true, // Default: true
debug: false, // Default: false
timeout: 30, // Default: 30 seconds
sampleRate: 1.0, // 0.0 to 1.0
maxBreadcrumbs: 20, // Default: 20
enableOfflineStorage: true, // Default: true
collectDeviceInfo: true, // Default: true
logHandler: (level, message, error, stackTrace) {}, // Optional
)| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | String | Yes | Your project API Key |
| environment | String | No | Environment name (default: "production") |
| release | String | No | App version identifier |
| captureUncaughtErrors | bool | No | Catch uncaught errors automatically (default: true) |
| enabled | bool | No | Enable/disable SDK (default: true) |
| debug | bool | No | Enable debug logging (default: false) |
| timeout | int | No | HTTP request timeout in seconds (default: 30) |
| sampleRate | double | No | Error capture sample rate 0.0-1.0 (default: 1.0) |
| maxBreadcrumbs | int | No | Max breadcrumbs to keep (default: 20) |
| enableOfflineStorage | bool | No | Keep errors that cannot be sent while the device is offline and send them later (default: true; not on web). See Offline Storage below |
| collectDeviceInfo | bool | No | Attach device, OS and app context to errors and messages (default: true) |
| apiUrl | String | No | Override the API URL (default: https://trace.abrova.ir) |
| logHandler | AbrovaTraceLogCallback | No | Receive the SDK internal log lines (level, message, error, stackTrace) |
Offline Storage
With enableOfflineStorage on (the default), errors that cannot be sent because the device is offline are kept on the device and sent later. Only errors are kept — messages, logs and performance data are not saved to disk.
- Native errors (Android, iOS) — the option is passed to the bundled native SDKs, which keep up to 100 unsent errors and send them later. Requires version 0.2.5 or newer.
- Dart errors — when a send fails with a network error or timeout, the error is saved to a file (up to 100; the oldest are dropped first) and re-sent the next time the app starts, with
AbrovaTrace.init()and withAbrovaTrace.initWithZone()alike (initWithZone()needs version 0.2.5 or newer). A stored error the server refuses for good is dropped instead of being retried at every start; it is kept when the server asks to try again later (HTTP 408, 429) or has a problem (5xx). - Web — nothing is stored.
Before 0.2.6 the SDK could print AbrovaTrace: Failed to initialize offline storage: Binding has not yet been initialized at start when you used initWithZone() with WidgetsFlutterBinding.ensureInitialized() inside the app function. It was harmless, and 0.2.6 no longer prints it: offline storage sets itself up once the app has started.
Error Information Captured#
The SDK automatically captures the following information with every error:
- Error Message — Exception message or custom message
- Stack Trace — Full stack trace for debugging
- Platform — Detected platform (flutter_web, flutter_android, etc.)
- Environment — Environment name (production, development, etc.)
- Release — App version/release identifier
- Timestamp — When the error occurred
- User ID, session ID, extras, tags and breadcrumbs — Whatever you set on the SDK
- Device context — Device model, OS and version, app version and build (when
collectDeviceInfois on; on web only locale and time zone) - User Agent — SDK version, platform and release (on web, the browser's user agent)
- URL — Current page URL (web only)
Complete Example#
import 'package:flutter/material.dart';
import 'package:abrova_trace_flutter_sdk/abrova_trace_flutter_sdk.dart';
void main() async {
await AbrovaTrace.initWithZone(
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: const String.fromEnvironment('ENV', defaultValue: 'development'),
release: '1.0.0',
captureUncaughtErrors: true,
sampleRate: 1.0,
maxBreadcrumbs: 20,
enableOfflineStorage: true,
debug: true,
),
() async {
WidgetsFlutterBinding.ensureInitialized();
// Auto-capture taps as breadcrumbs (all platforms)
AbrovaTrace.enableGestureBreadcrumbs();
// Enable logging
AbrovaTrace.enableLogging(
sourceId: 'my-flutter-app',
sourceName: 'My Flutter App',
);
runApp(MyApp());
},
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
AbrovaTrace.setUserId('user_123');
AbrovaTrace.setTag('screen', 'HomeScreen');
AbrovaTrace.addNavigationBreadcrumb('Launch', 'HomeScreen');
}
Future<void> _handlePurchase() async {
AbrovaTrace.addUserBreadcrumb('Initiated purchase', data: {'product': 'Premium'});
try {
await AbrovaTrace.trackOperation('process_purchase', () async {
await processPurchase();
});
AbrovaTrace.info('Purchase successful', metadata: {'amount': 99.99});
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace,
message: 'Purchase failed',
extra: {'user_id': 'user_123', 'screen': 'HomeScreen'},
);
}
}
Future<void> processPurchase() async {
// ... purchase logic
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My App')),
body: Center(
child: ElevatedButton(
onPressed: _handlePurchase,
child: const Text('Purchase'),
),
),
);
}
}Best Practices#
Initialize AbrovaTrace before runApp() to catch all errors from app start
Use initWithZone for better async error catching in production apps
close() flushes logs and tears the SDK down; native crash detection does not depend on it
Call setUserId() after login to associate errors with users
Add breadcrumbs for important user actions (purchases, navigation, form submissions)
Call enableGestureBreadcrumbs() to auto-capture taps. Critical for Flutter Web where stack traces can be partial
Tag checkout buttons, payment fields, and key actions with ValueKey so they are identifiable in tap breadcrumbs
Set tags like team, module, or feature for easy error filtering in the dashboard
Enable logging right after init() to capture all events from app start
Separate production, staging, and development errors using the environment config
Set debug: true during development to see SDK logs and verify integration