brevwick
Flutter SDK for Brevwick — send rich QA feedback from inside your app by wrapping it in a single widget. Bug reports arrive with a screenshot, the recent console / network / route history, and device context already attached, so you spend less time asking "what were you doing when it broke?".
Runs on all six Flutter platforms — Android, iOS, macOS, Linux, Windows, and web.
Install
flutter pub add brevwick
Quick start
Wrap your app once. Brevwick.runGuarded captures uncaught errors,
BrevwickScreenshotScope enables screenshot capture, and BrevwickOverlay
adds the feedback affordance.
import 'package:brevwick/brevwick.dart';
import 'package:flutter/material.dart';
void main() {
Brevwick.runGuarded<void>(() async {
WidgetsFlutterBinding.ensureInitialized();
await Brevwick.install(
BrevwickConfig(
projectKey: 'pk_live_...',
environment: 'prod',
// Optional: forward the SDK's internal diagnostics into your logger.
onLog: (level, message, {error, stackTrace}) =>
debugPrint('[brevwick] ${level.name}: $message'),
),
);
runApp(const MyApp());
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// BrevwickScreenshotScope wraps the whole app so any route can be
// captured.
return BrevwickScreenshotScope(
child: MaterialApp(
navigatorObservers: [Brevwick.instance.routeObserver()],
// BrevwickOverlay must sit BELOW a Navigator: its launcher shows a
// Tooltip and opens a modal bottom sheet, both of which need a
// Navigator + Overlay ancestor. Wrap your screen (`home`) — or, for
// an overlay that persists across routes, a go_router `ShellRoute`
// body. Do NOT place it in `MaterialApp.builder`, which sits ABOVE
// the Navigator (the launcher would throw `No Overlay widget found`).
home: BrevwickOverlay(child: const HomePage()),
),
);
}
}
Placement.
BrevwickOverlayneeds aNavigator+Overlayancestor.home:/ a routed screen / aShellRoutebody all qualify;MaterialApp.builderdoes not.BrevwickScreenshotScopehas no such requirement — keep it at the top (aroundMaterialAppor in itsbuilder) so every route is capturable.
Launcher presentation
⚠️ Default change. The zero-config launcher is now a vertical tab flush against the right screen edge, vertically centered, with a rotated "Feedback" label — not the old bottom-right corner bubble. The bubble remains fully supported: any call site that passes an explicit corner
position(e.g.Alignment.bottomRight) keeps compiling and keeps the bubble at that corner. To keep the bubble with no other configuration, passvariant: BrevwickLauncherVariant.bubble.
BrevwickOverlay(child: app) // NEW default: right-edge tab
BrevwickOverlay(side: BrevwickLauncherSide.left, child: app) // left-edge tab
BrevwickOverlay(compact: true, child: app) // icon-only edge tab
BrevwickOverlay(offset: 80, child: app) // tab nudged 80 px down
BrevwickOverlay(
variant: BrevwickLauncherVariant.bubble, // legacy corner bubble
child: app,
)
BrevwickOverlay(
position: Alignment.bottomLeft, // legacy call site: still a bubble
child: app,
)
How variant / position / side resolve:
variant |
position / side |
Result |
|---|---|---|
| null | both null | tab, right edge (new default) |
| null | position non-null |
bubble at exactly that Alignment (legacy behaviour, including non-corner alignments) |
| null | side non-null |
tab on that edge |
.tab |
any | tab; edge = side if set, else the position alignment's horizontal side (default right). Tabs are always vertically centered (± offset) |
.bubble |
any | bubble at position ?? Alignment.bottomRight; side and offset are ignored |
An explicit variant always wins. label (default 'Feedback') is the tab's
rotated text and its tooltip/semantics label; compact drops the visible text
(the Flutter bubble is already icon-only, so compact is a no-op there).
To capture failing HTTP calls in the network ring, add the interceptor to your Dio instance:
final dio = Dio()..interceptors.add(Brevwick.instance.dioInterceptor());
What gets attached
Every report carries, automatically:
- Screenshot of the screen the user was on when they tapped the feedback button (captured before the composer opens, so the sheet itself isn't in the frame).
- Breadcrumb rings — the recent
consolelogs,networkrequests, androutetransitions, each toggleable viaRingFlagsonBrevwickConfig. - Device context — platform, OS, locale, viewport, and SDK version.
- User context — anything you supply via
BrevwickConfig.user/userContext.
Users can add a title, expected/actual notes, and up to a handful of attachments (10 MB each), and optionally let AI format the report. The issue title is derived from the first line of the description (the API requires one); AI formatting, when enabled, refines it server-side.
App-id allowlist
If your project restricts submissions to known apps (an AllowedAppIDs
list), the SDK sends the host app's platform package id — the Android
applicationId / iOS bundle id, read from package_info_plus — as the
X-Brevwick-App-Id header on every ingest call. Register that id (e.g.
com.acme.app, or com.acme.app.dev for a flavored build) in the
project's allowlist, otherwise the API responds 403 ORIGIN_NOT_ALLOWED
(app_id_mismatch). Projects without an app-id allowlist need no setup —
the header is informational and the value is omitted when unavailable
(e.g. some web builds).
Forwarding SDK diagnostics
Set BrevwickConfig.onLog to route the SDK's own diagnostics — project-config
fetch failures, screenshot-capture warnings, uncaught zone errors, and submit
failures — into your app's logger. Messages are redacted at the boundary, and a
throwing sink can never break the SDK.
await Brevwick.install(
BrevwickConfig(
projectKey: 'pk_live_...',
onLog: (level, message, {error, stackTrace}) {
switch (level) {
case BrevwickLogLevel.error:
myLogger.error(message, error, stackTrace);
case BrevwickLogLevel.warn:
myLogger.warn(message);
case BrevwickLogLevel.info:
case BrevwickLogLevel.debug:
myLogger.info(message);
}
},
),
);
Privacy & redaction
Every payload runs through a redaction pass before it leaves the device —
emails, tokens, and other sensitive values are scrubbed client-side. Set
fingerprintOptOut: true on BrevwickConfig to suppress device-fingerprint
signals.
Platform view caveat
The screenshot layer cannot capture AndroidView / UiKitView (maps,
webviews, and other embedded platform views), which appear as gaps in the
captured frame. Document this for any screen that embeds them.
Contract
The canonical SDK contract lives in
brevwick-ops/docs/brevwick-sdd.md § 12.
The wire format mirrors the brevwick-sdk-js
SDK byte-for-byte.
License
MIT — see LICENSE.
Libraries
- brevwick
- Brevwick Flutter SDK.