bugban_flutter 1.1.0
bugban_flutter: ^1.1.0 copied to clipboard
Bugban crash reporting and performance monitoring for Flutter — Flutter errors, real screenshots, an offline queue and start-up / screen-load timing.
bugban_flutter #
Crash reporting and performance monitoring for Flutter, from Bugban.
flutter pub add bugban_flutter
import 'package:bugban_flutter/bugban_flutter.dart';
void main() {
BugbanFlutter.run(
options: BugbanOptions(
apiKey: 'bb_your_project_key',
host: 'https://bugban.online',
environment: 'production',
release: '1.0.0',
),
appRunner: () => runApp(const MyApp()),
);
}
Why run and not just init #
Flutter has four separate error paths, and an integration that wires up only the obvious one silently misses whole classes of crash:
| Path | Catches |
|---|---|
FlutterError.onError |
errors inside build, layout and paint |
PlatformDispatcher.instance.onError |
uncaught async errors (Flutter 3.3+) |
runZonedGuarded |
everything else on the zone |
Isolate.current.addErrorListener |
crashes on a background isolate |
BugbanFlutter.run installs all four. runZonedGuarded only catches errors
raised inside its callback, so initialising outside the zone and calling
runApp inside it loses async errors without a word — which is why run owns
both steps.
If your app already has its own zone, use BugbanFlutter.init(...) instead and
keep the zone handler yourself:
await BugbanFlutter.init(options);
runZonedGuarded(() => runApp(const MyApp()), (e, s) {
Bugban.capture(e, stackTrace: s, handled: false);
});
Every handler is chained, never replaced. In debug the existing one draws the red screen and in release your app may have its own reporter on it; taking either away silently is not something a monitoring SDK gets to do.
Screen names #
MaterialApp(
navigatorObservers: [BugbanNavigatorObserver()],
routes: {'/': (_) => const HomePage(), '/checkout': (_) => const CheckoutPage()},
)
Without it a report names a widget class; with it, it says it broke on
/checkout. The same observer times screen transitions (SCREEN_LOAD).
Screenshots #
Off by default, and that default is deliberate: a screenshot is pixels, so once it is taken nothing downstream can tell a password field from any other grey rectangle. Masking has to happen before the capture.
// 1. opt in
BugbanOptions(/* ... */, captureScreenshots: true)
// 2. mark what to capture
RepaintBoundary(key: BugbanScreenshot.rootKey, child: MaterialApp(/* ... */))
// 3. cover what must not be seen
BugbanMask(child: TextField(obscureText: true))
No extra package needed — Flutter's own RepaintBoundary rasterises the tree.
Performance #
Core Web Vitals do not translate to a phone; these do:
APP_START— how long until the app is usable. CallBugbanFlutter.markAppReady()when your first screen has its data; if you never do, the first frame is used instead.SCREEN_LOAD— time to move to another screen (from the navigator observer).JANK— the longest stuttering frame.
Offline queue #
On by default, backed by shared_preferences. A phone loses signal in a lift
and a crash can end the process before a single request completes, so events
are written to disk before the request goes out and cleared only once the
server has taken them.
Not covered yet #
Native crashes (ANR, SIGSEGV) never reach the Dart layer and are not captured. Dart errors, async errors, isolate crashes and caught exceptions all are.
Licence #
MIT