Skip to content

Flutter SDK

The Pionne SDK for Flutter. Auto-captures FlutterError errors and async exceptions via Zone, enriches every event with device context.

pubspec.yaml
dependencies:
pionne_flutter: ^0.3.3
Terminal window
flutter pub get
import 'package:flutter/material.dart';
import 'package:pionne_flutter/pionne_flutter.dart';
void main() {
Pionne.init(
token: 'pio_live_xxx',
release: '1.0.0',
environment: 'production',
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(home: MyHome());
}
  • FlutterError.onError — Flutter framework errors (build, layout, paint)
  • Zone runner (via runZonedGuarded) — uncaught Dart async exceptions
  • PlatformDispatcher.instance.onError (Flutter 3.3+) — Dart-realm errors not caught by Flutter

Dart handlers can only see crashes in the Dart isolate. A native crash tears down the whole process before any Dart can run. Since 0.4.0, the SDK ships native iOS + Android code that records these via the OS and replays them as fatal events on the next launch.

On by default. To disable:

Pionne.init(PionneOptions(
token: 'pio_live_…',
captureNativeCrashes: false,
));

Via MetricKit (MXCrashDiagnostic):

  • Objective-C / Swift NSException — exception name + composed message on iOS 17+ (e.g. NSInvalidArgumentException)
  • SignalsSIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE, SIGTRAP
  • Out-of-memory kills and watchdog terminations (0x8badf00d)
  • Call stack tree — system frames symbolicated by the OS; app frames as binaryName 0xADDRESS

Via ActivityManager.getHistoricalProcessExitReasons():

  • REASON_CRASH — unhandled JVM exception
  • REASON_CRASH_NATIVE — NDK / native (C/C++) crash
  • REASON_ANR — Application Not Responding (with the ANR trace)
  • REASON_LOW_MEMORY — OOM kill

Native crashes arrive on the launch after the crash (the OS delivers them post-mortem), with mechanism.type = "native" and a native.source tag (metrickit on iOS, app_exit on Android).

  • Platform.operatingSystemios, android, linux, etc.
  • Platform.operatingSystemVersion — OS version
  • package_info_plus — app version, build number, package name (optional peer dep)
  • device_info_plus — model, manufacturer (optional peer dep)
try {
await fetchData();
} catch (error, stackTrace) {
await Pionne.captureException(
error,
stackTrace: stackTrace,
tags: {'feature': 'sync'},
contexts: {'sync': {'retry': 2}},
);
}
Pionne.init(token: '…', release: '…', environment: '…');
Pionne.captureException(error, stackTrace: stack, tags: {});
Pionne.captureMessage('Cache miss', level: PionneLevel.warning);
Pionne.setUser(id: 'user_42');
Pionne.setTags({'region': 'eu'});
Pionne.setEnabled(false);
Pionne.addBreadcrumb(category: 'navigation', message: '/checkout');
PlatformStatus
iOSSupported
AndroidSupported
WebSupported
macOSSupported
WindowsSupported
LinuxSupported

Pionne can attach the user’s approximate city/region/country to every event. Off by default for privacy.

Pionne.init(PionneOptions(
token: 'pio_live_xxx',
sendGeography: true, // ← opt-in
));

At startup, a single HTTP call to https://ipapi.co/json/ (4 s timeout) resolves contexts.geo = { city, region, country, country_code } and attaches it to every subsequent event. No iOS/Android permission required (no GPS, just IP reverse-lookup). If the lookup fails, the SDK keeps working without geo.

For a custom provider: geographyEndpoint: 'https://geo.yourapi.com/'.

The package_name (e.g. com.yourapp.app) is auto-pinned server-side on the first event. If your token leaks and gets used from another app, those events are rejected.