brevwick 0.6.0
brevwick: ^0.6.0 copied to clipboard
Brevwick SDK for Flutter — send QA feedback from your app in one widget wrap.
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.