Flutter SDK
iOS and Android from one pub.dev package. Shake the phone, annotate, send — reports land in the same inbox as your web app's.
Install
flutter pub add tracebird_flutterimport 'package:tracebird_flutter/tracebird_flutter.dart';
void main() async {
await Tracebird.init(
projectKey: 'pk_live_a1b2c3',
environment: Env.name,
release: '1.4.2',
trigger: TracebirdTrigger.shake,
capture: const TracebirdCapture(
logs: true,
network: true,
breadcrumbs: true,
),
privacy: const TracebirdPrivacy(
maskWidgets: true,
scrubHeaders: ['authorization'],
),
);
runApp(
TracebirdWrapper( // installs the overlay + root RepaintBoundary
child: const MyApp(),
),
);
}com.acme.app, com.acme.beta) to the project's allowlist in Settings — the mobile equivalent of the web origin allowlist.Options
| option | type | default | description |
|---|---|---|---|
| projectKey | String | — | Required — same key as web, or a dedicated mobile project. |
| trigger | TracebirdTrigger | shake | shake · floatingButton · manual. Shake keeps the UI clean during usability tests. |
| capture.logs | bool | true | debugPrint interceptor, plus adapters for logger and talker. |
| capture.network | bool | true | Dio interceptor provided; http adapter optional. Method, URL, status, duration — never bodies. |
| capture.breadcrumbs | bool | true | NavigatorObserver route trail — how the tester got to this screen. |
| privacy.maskWidgets | bool | true | Widgets wrapped in TracebirdMask are blanked in every capture. |
| privacy.scrubHeaders | List<String> | — | Headers stripped from captured requests. |
Masking sensitive widgets
TracebirdMask(
child: PatientCard(patient: patient),
)Masked widgets are blanked at capture time, on the device — the real pixels never enter a screenshot.
Triggers
- Shake (default) — accelerometer with a calibrated threshold and 2 s debounce; on iOS the native
UIEventSubtypeMotionShakeis used for reliability. - Floating button — the classic bubble, for desktop or simulator testing.
- Manual — trigger from your own UI:
Tracebird.report(type: FeedbackType.bug);Screens with maps, webviews, video
A platform view — a WebView, a Google Map, a camera preview — is not drawn by Flutter, and a naive capture leaves a hole where it sits. Measured on a real WebView on both platforms:
| option | type | default | description |
|---|---|---|---|
| iOS | needs the fallback | — | RepaintBoundary leaves a hole; UIGraphicsImageRenderer with drawHierarchy captures the view correctly. |
| Android | no fallback needed | — | Platform views are composited into the Flutter scene, so RepaintBoundary already contains them. |
So the SDK uses Flutter's own capture everywhere and pays for a native capture only on iOS, only on the screens where it detects a platform view. You configure none of it. Content protected by FLAG_SECURE still cannot be captured on Android — the report is sent without a screenshot.
Offline testers
Mobile testers lose network. Reports are persisted locally and replayed on reconnection, with an idempotency key so flaky retries never create duplicates in your inbox.
What a mobile report contains
- Annotated screenshot (brush, arrow, rect and a blur that destroys the pixels on the device)
- Logs and failed requests, current route + navigation breadcrumbs
- Device model, OS version, app version + build number, locale, connectivity
- Custom metadata via
Tracebird.addMetadata()
platform in the dashboard when you need to split them.