Abrova
☰ Docs menu · Flutter SDK

Flutter SDK

Track errors, breadcrumbs, logs, performance, and native crashes in Flutter apps across Web, Android, iOS, Desktop, and Chrome Extensions.

Flutter 0.2.6Dart 3.8+Android API 19+iOS 12+Last updated: 27 Oct 2026
Simple Setup

Initialize with just a few lines of code

Multi-Platform

Works on Web, Android, iOS, and Chrome Extensions

Automatic Error Capture

Catches uncaught errors automatically

Manual Error Reporting

Capture custom errors and messages

Platform Detection

Automatically detects the running platform

Configurable

Flexible configuration options

Rich Context

Add custom data, tags, and user context to errors

Breadcrumbs

Track user actions and events before errors

Logging

Structured log ingestion with batching and levels

Performance Tracking

Track HTTP requests and custom operations

Native Crash Detection

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:

yaml
# pubspec.yaml
dependencies:
  abrova_trace_flutter_sdk:
    hosted: https://center.abrova.ir/api/packages/abrova/pub
    version: ^0.2.6

2. 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):

groovy
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://center.abrova.ir/api/packages/abrova/maven" }
    }
}

iOS: add this inside the app target in ios/Podfile:

ruby
pod 'AbrovaTrace', :git => 'https://center.abrova.ir/abrova/abrova-trace-ios-sdk.git', :tag => '0.2.4'

Then install:

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

Which key?

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

dart
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)

Recommended.

Using initWithZone catches async errors that would otherwise be missed. This is the recommended approach for production apps.

dart
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

dart
try {
  riskyOperation();
} catch (e, stackTrace) {
  await AbrovaTrace.captureException(
    e,
    stackTrace: stackTrace,
    message: 'Failed to perform risky operation',
    extra: {'user_id': 'user123'},
  );
}

Capture Messages

dart
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'},
);
Where messages show up.

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

dart
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

dart
// Set after user login
AbrovaTrace.setUserId('user_123');

// Get current user ID
final userId = AbrovaTrace.getUserId();

Custom Extra Data

dart
// 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

dart
// 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 track user actions and events leading up to an error, helping you understand what happened before the crash.

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

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

json
{
  "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:

dart
IconButton(
  key: const ValueKey('cart-remove-row-2'),  // ← surfaces in breadcrumb
  icon: const Icon(Icons.delete),
  onPressed: _removeItem,
)

Disable

dart
AbrovaTrace.disableGestureBreadcrumbs();
Privacy.

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:

dart
// 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

dart
// 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 reported

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

dart
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

dart
final result = await AbrovaTrace.trackOperation(
  'load_user_profile',
  () async {
    final user = await fetchUser();
    await cacheUser(user);
    return user;
  },
  operationType: 'custom',
);

Manual Span Reporting

dart
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

dart
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

dart
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

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

  1. iOS — PLCrashReporter records signal and Mach exception crashes to disk
  2. Android — an uncaught exception handler, plus ApplicationExitInfo (Android 11 / API 30+) for native crashes and ANRs
  3. 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.

Shutting down.

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#

dart
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
)
OptionTypeRequiredDescription
apiKeyStringYesYour project API Key
environmentStringNoEnvironment name (default: "production")
releaseStringNoApp version identifier
captureUncaughtErrorsboolNoCatch uncaught errors automatically (default: true)
enabledboolNoEnable/disable SDK (default: true)
debugboolNoEnable debug logging (default: false)
timeoutintNoHTTP request timeout in seconds (default: 30)
sampleRatedoubleNoError capture sample rate 0.0-1.0 (default: 1.0)
maxBreadcrumbsintNoMax breadcrumbs to keep (default: 20)
enableOfflineStorageboolNoKeep errors that cannot be sent while the device is offline and send them later (default: true; not on web). See Offline Storage below
collectDeviceInfoboolNoAttach device, OS and app context to errors and messages (default: true)
apiUrlStringNoOverride the API URL (default: https://trace.abrova.ir)
logHandlerAbrovaTraceLogCallbackNoReceive 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 with AbrovaTrace.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 collectDeviceInfo is 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#

dart
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 Early

Initialize AbrovaTrace before runApp() to catch all errors from app start

Use initWithZone

Use initWithZone for better async error catching in production apps

Call close() Only on Shutdown

close() flushes logs and tears the SDK down; native crash detection does not depend on it

Set User ID After Auth

Call setUserId() after login to associate errors with users

Add Breadcrumbs

Add breadcrumbs for important user actions (purchases, navigation, form submissions)

Enable Gesture Breadcrumbs

Call enableGestureBreadcrumbs() to auto-capture taps. Critical for Flutter Web where stack traces can be partial

Add ValueKey to Important Widgets

Tag checkout buttons, payment fields, and key actions with ValueKey so they are identifiable in tap breadcrumbs

Use Tags for Filtering

Set tags like team, module, or feature for easy error filtering in the dashboard

Enable Logging Early

Enable logging right after init() to capture all events from app start

Use Different Environments

Separate production, staging, and development errors using the environment config

Enable Debug Mode in Development

Set debug: true during development to see SDK logs and verify integration

Was this page helpful?
Report a problem