Flutter Quick Start
Integrate Traceway into your Flutter application with the traceway (opens in a new tab) package from pub.dev.
Installation
flutter pub add tracewaySetup
Use Traceway.run() to wrap your app. This initializes the SDK and captures errors automatically:
import 'package:flutter/material.dart';
import 'package:traceway/traceway.dart';
void main() {
Traceway.run(
connectionString: 'your-token@https://traceway.example.com/api/report',
options: TracewayOptions(
screenCapture: true,
version: '1.0.0',
),
child: MyApp(),
);
}Capture Errors Manually
import 'package:traceway/traceway.dart';
// Capture a caught exception
try {
await riskyOperation();
} catch (e, st) {
TracewayClient.instance?.captureException(e, st);
}
// Capture a message
TracewayClient.instance?.captureMessage('User completed onboarding');With Options
TracewayOptions accepts the following parameters:
| Option | Type | Default | Description |
|---|---|---|---|
sampleRate | double | 1.0 | Event sampling rate (0.0 to 1.0) |
screenCapture | bool | false | Records the last ~10 seconds of screen as video |
debug | bool | false | Prints debug info to the console |
version | String | '' | App version string |
debounceMs | int | 1500 | Milliseconds before sending batched events |
capturePixelRatio | double | 0.75 | Screenshot resolution scale |
maxBufferFrames | int | 150 | Max frames in recording buffer (~10s at 15fps) |
fps | int | 15 | Frames per second for screen capture (1–59) |
retryDelayMs | int | 10000 | Retry delay in ms on failed uploads |
maxPendingExceptions | int | 5 | Max exceptions held in memory before oldest is dropped |
persistToDisk | bool | true | Persist pending exceptions to disk so they survive app restarts |
maxLocalFiles | int | 5 | Max exception files stored on disk |
localFileMaxAgeHours | int | 12 | Hours after which unsynced local files are deleted |
captureLogs | bool | true | Mirror every print / debugPrint into the rolling log buffer |
captureNetwork | bool | true | Install HttpOverrides.global to record every dart:io HTTP call |
captureNavigation | bool | true | Record navigation transitions reported by Traceway.navigatorObserver |
eventsWindow | Duration | 10s | Rolling window kept in the log/action buffers |
eventsMaxCount | int | 200 | Hard cap applied independently to logs and actions |
Traceway.run(
connectionString: 'your-token@https://traceway.example.com/api/report',
options: TracewayOptions(
sampleRate: 0.5,
screenCapture: true,
debug: true,
version: '2.1.0',
debounceMs: 2000,
capturePixelRatio: 0.75,
),
child: MyApp(),
);Platform Setup
Traceway needs network access to send error reports. Depending on the platform, you may need to add permissions manually.
Android
Add the INTERNET permission to android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
...macOS
macOS apps are sandboxed by default and cannot make network requests without the com.apple.security.network.client entitlement.
Add it to both macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements:
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>iOS
No additional configuration is required. iOS apps can make HTTPS requests by default.
Screen Recording
When screenCapture is set to true:
- The SDK wraps the app in a
RepaintBoundaryand captures frames at approximately 15 fps. - On exception, the last ~10 seconds are encoded to MP4 and sent alongside the error report.
- Touch and click positions are rendered as blue circles on the captured frames. These overlays are invisible to the user during normal use.
- Recordings appear in the Traceway dashboard alongside the stack trace, giving you full visual context for every error.
Logs & Actions
Every captured exception ships with the last ~10 seconds of session context, attached to the same session recording as the video. Two independent rolling buffers — logs and actions — are kept in memory, each capped at 200 entries by default.
Logs
Every print and debugPrint line is mirrored into the log buffer via a Zone print override installed by Traceway.run(). Console output behaves normally; it just gets a copy.
dart:developer.log is not captured — it doesn't flow through the Zone print hook.
Actions
Three kinds of actions are collected:
-
Network — every dart:io HTTP request (method, URL, status, duration, request/response byte counts).
HttpOverrides.globalis installed byTraceway.run(), so this catchespackage:http, Dio, Firebase, and any other library that sits on the dart:io HttpClient on native platforms. -
Navigation — push / pop / replace / remove transitions, recorded by
Traceway.navigatorObserver. Wire it into your app's navigator:MaterialApp( navigatorObservers: [Traceway.navigatorObserver], home: const HomePage(), ); -
Custom — anything your app records explicitly:
Traceway.recordAction( category: 'cart', name: 'add_item', data: {'sku': 'SKU-123', 'qty': 2}, );
Wire shape
Logs and actions are sent to the backend inside the same sessionRecordings entry that carries the MP4 — they're stored under separate logs and actions arrays. Each recording also carries startedAt and endedAt ISO 8601 timestamps so the backend can align events onto the video timeline (offsetIntoVideoMs = event.timestamp − recording.startedAt):
{
"sessionRecordings": [
{
"exceptionId": "...",
"startedAt": "2026-04-28T14:30:53.011Z",
"endedAt": "2026-04-28T14:31:02.314Z",
"events": [{"type": "flutter_video", "data": "...", "fps": 15}],
"logs": [
{"type": "log", "timestamp": "2026-04-28T14:30:54.508Z", "level": "info", "message": "user tapped pay"}
],
"actions": [
{"type": "navigation", "action": "push", "from": "/", "to": "/cart", "timestamp": "2026-04-28T14:30:53.011Z"},
{"type": "network", "method": "GET", "url": "...", "statusCode": 200, "durationMs": 86, "timestamp": "2026-04-28T14:30:54.422Z"},
{"type": "custom", "category": "cart", "name": "add_item", "data": {"sku": "SKU-1"}, "timestamp": "2026-04-28T14:30:54.605Z"}
]
}
]
}When the screen recorder is on, startedAt and endedAt come from the first and last buffered video frames. When there's no video (e.g. screenCapture: false with logs/actions only), they fall back to the timestamp range of the buffered events. A session recording entry is created for every captured exception that has any timeline data.
Network capture on Flutter web
HttpOverrides.global does not run on Flutter web. Use TracewayHttpClient instead — it's a drop-in http.Client you can use directly or pass to libraries that accept a custom client (Dio, Chopper, etc.):
import 'package:traceway/traceway.dart';
final client = TracewayHttpClient();
final res = await client.get(Uri.parse('https://api.example.com/users'));Disabling channels
Each channel can be turned off individually via TracewayOptions:
Traceway.run(
connectionString: 'your-token@https://traceway.example.com/api/report',
options: TracewayOptions(
screenCapture: true,
captureLogs: true, // print / debugPrint
captureNetwork: true, // HttpOverrides
captureNavigation: true, // NavigatorObserver
),
child: MyApp(),
);Privacy Masking
Use TracewayPrivacyMask to hide sensitive content in screen recordings. Masked regions are applied to the recorded frames only — the user sees no visual change in the live app.
Blur (pixelation):
TracewayPrivacyMask(
child: Text('4242 4242 4242 4242'),
)The default mode is TracewayMaskMode.blur() which pixelates the region. You can control the intensity with the ratio parameter (0.0 = light, 1.0 = heavy):
TracewayPrivacyMask(
mode: TracewayMaskMode.blur(ratio: 0.5),
child: CreditCardWidget(),
)Blank (solid fill):
TracewayPrivacyMask(
mode: TracewayMaskMode.blank(color: Color(0xFF000000)),
child: SensitiveDataWidget(),
)This replaces the region with a solid color (black by default) in the recording.
What Gets Captured Automatically
Traceway.run() sets up capture for the following error sources with no additional configuration:
- Flutter framework errors — rendering, layout, and gesture errors reported through
FlutterError.onError. - Platform errors — native platform channel errors.
- Uncaught async errors — any unhandled exceptions in asynchronous code, captured via Dart's
Zonemechanism.
Release Symbolication (obfuscated builds)
A plain flutter build --release keeps readable stack traces, so there's nothing to do. If you harden a release with --obfuscate and/or --split-debug-info, the binary no longer carries names and traces arrive as bare instruction offsets. Upload the build's .symbols files and Traceway symbolicates those traces server-side.
The SDK ships an uploader. Configure it once in pubspec.yaml:
traceway:
url: https://your-traceway-host # omit on Traceway Cloud
# upload_token: ... # prefer the env var in CIThe upload token is your project's upload token from the dashboard (the same one used for JS source maps), not the runtime token in your connection string. Then on every release:
flutter build apk --release --obfuscate --split-debug-info=build/symbols
TRACEWAY_UPLOAD_TOKEN=your-token dart run traceway:upload_symbolsThe uploader auto-discovers build/symbols, so no flags are required. See Dart symbolication for the full reference (flags, Apple debug IDs, the upload API) and how resolution works.
Test Your Integration
Add a button that throws an error to verify everything is wired up:
ElevatedButton(
onPressed: () {
throw StateError('Test error from Traceway');
},
child: Text('Send Test Error'),
)Tap the button and check your Traceway dashboard to verify the error appears.
Platform Support
| Platform | Error Tracking | Screen Recording |
|---|---|---|
| iOS | Yes | Yes |
| Android | Yes | Yes |
| macOS | Yes | Yes |
| Web | No | No |
For Flutter web, use the JS SDK instead — see Flutter Web below.
Flutter Web
This SDK does not support Flutter web. For web apps, use the @tracewayapp/frontend JS SDK which provides rrweb session replay and automatic fetch instrumentation.
Add the CDN script to web/index.html:
<script src="https://cdn.jsdelivr.net/npm/@tracewayapp/frontend@1/dist/traceway.iife.global.js"></script>
<script>
Traceway.init("your-token@https://traceway.example.com/api/report");
</script>Alternatively, if you use npm in your Flutter web project:
npm install @tracewayapp/frontend<script type="module">
import { init } from '@tracewayapp/frontend';
init('your-token@https://traceway.example.com/api/report');
</script>See the full JS SDK documentation for all options including session recording, error filtering, and distributed tracing.