outcode_bug_reporter 2.1.1
outcode_bug_reporter: ^2.1.1 copied to clipboard
In-app bug reporter for Flutter: a floating button that captures the screen, lets users annotate and redact it, set a severity, and file it to ClickUp or any backend.
outcode_bug_reporter #
2.1.1 #
Fixed #
- The reporter now works in release builds at all. Capture read
RenderRepaintBoundary.debugNeedsPaint, which assigns its result inside anassert. Asserts are stripped from release builds, so reading it threwLateInitializationError, and because the read sat outside the surroundingtrythe error escapedcaptureBoundary(), propagated out ofopen(), and was swallowed by its.ignore(). In any release or profile build, tapping the floating button hid it and nothing else ever happened — no error, and no way to open the reporter again for the rest of the session. Debug builds were unaffected, which is why it went unnoticed:flutter testruns with asserts enabled, so no widget test can reproduce it. The check is now guarded bykDebugMode, the whole capture body sits inside thetry, andopen()reports a thrown capture throughFlutterError.reportErrorand resets its state so it can never latch again. If you are on 2.1.0, upgrade — the reporter does not function in your shipped builds. - The floating button no longer lags behind your finger. Dragging accumulated each
onPanUpdatedelta onto a field that is only written duringbuild(), but Flutter flushes every queued pointer event in one synchronous pass with no build in between — so all but the last event in a frame read the same stale base and threw their delta away. On a high-refresh digitizer that meant the button travelled a fraction of the distance the finger did, falling further behind for the whole gesture. Position is now driven fromdetails.globalPositionminus the grab offset, which is idempotent per event.dragStartBehavior: DragStartBehavior.downalso recovers the first 36 logical pixels (kPanSlop) that the tap/pan arena previously discarded, so the drag starts where your finger does. Re-grabbing mid snap-back no longer jumps, and the button is wrapped in aRepaintBoundaryso its shadow and halo stop re-rasterizing the app behind it. - The report form no longer claims "Screenshot attached" when the capture produced nothing; it says so instead, and hides the edit affordance that led to an empty editor.
Added #
BugReporterConfig.fabColor— colours the floating button and its halo only, leaving the rest of the flow on the theme accent. The button's icon colour is derived from it, so a light brand colour gets a dark glyph instead of white-on-white.BugReporterTheme.withAccent(Color)— a copy with the accent applied andaccentPress,ringandonAccentderived from it. Prefer it overcopyWith(accent: ...), which leaves those three on the preset's values and so leaves an indigo halo behind a custom accent. MirrorsderiveAccentTokens()in the JS core.- Device brand and model rows. Supply
deviceInfo['brand']/['manufacturer']/['model'](e.g. fromdevice_info_plus) and reports gainBrand nameandModelrows. Brands are title-cased and fall back to the manufacturer when a carrier has rewritten them; the model string is kept verbatim. The package still takes no plugin dependencies —normalizeDeviceIdentity()mirrors the JS core's helper and only cleans up what you inject.
Changed #
- Context row labels are 78px wide (was 74px), matching the web and React Native forms so a label like "Brand name" is not clipped.
2.1.0 #
Added #
- Shake to report.
BugReporterConfig.shaketakes aShakeOptionswith anaccelerometercallback; shaking the device opens the reporter. It stays off until you provide the stream, because the package still takes no plugin dependencies — one expression withsensors_plus, or any sample source you already have.ShakeDetectoris exported on its own: pure Dart, no timers, no subscriptions, and driven by an injectable clock so it is unit-tested against a fake one. BugReporterConfig.showButton(defaulttrue) — setfalsefor a shake-only or controller-only build, with no visible bug button.
The detector shares its defaults with @outcode/bug-reporter-native (packages/core/src/shake.ts),
so the gesture feels identical on both. It needs about a second of sustained shaking past 1.2g and
then goes quiet for three seconds, so a phone set down hard doesn't file a bug. The reporter stops
listening while a report is open and while the app is backgrounded, and mutes when the flow closes so
the shake that dismissed it can't reopen it.
Security #
- Redaction now fails closed. The annotate step flattens annotations into the screenshot before submitting. If that flatten failed, the editor fell back to the original capture — so a report where the user had redacted a password or a customer name could file that content unredacted, silently and with the normal success flow. When flattening fails and at least one redaction block was drawn, the submit is now aborted, the editor stays open, and an error explains that nothing was sent; Continue retries. Reports with no redaction still fall back to the unflattened capture, which is safe — there was nothing hidden to lose.
Fixed #
Fixes for the two pub.flutter-io.cn score deductions on 2.0.0 (140/160). Both needed a published version to take effect, so they ship here.
- Shortened the package description to 166 characters; pub.flutter-io.cn requires 60–180 and 2.0.0 shipped 205.
- Added
example/back inside the package so pub.flutter-io.cn's Example tab is populated — a minimalMaterialApp.builderintegration, with the full demo staying atexample-flutter/in the repo root.
The third deduction is not in the package: pub.flutter-io.cn reports the
homepage,repository, andissue_trackerURLs as unreachable becausegithub.com/OutCode-Software/bug-reporterreturns 404. Publishing the repository fixes that on pub.flutter-io.cn's next analysis, with no release needed.
2.0.0 #
First release of the Flutter package.
Ported from @outcode/bug-reporter-core + @outcode/bug-reporter-native, and versioned 2.0.0 to
join the rest of the family at the same number — a 2.0.0 report looks the same whether it was filed
from Flutter, the web, or React Native.
Added #
BugReporter— one widget, mounted inMaterialApp.builder, running the fullidle → capture → annotate → form → submitting → doneflow, plusBugReporterControllerfor opening it from your own code andenabledfor compiling it out of release builds.- Screenshot capture via Flutter's own
RepaintBoundary— no plugin, and no platform channel, so Android, iOS, web, macOS, Windows, and Linux all work out of the box.ScreenshotOptions.maxWidthis applied as a capture pixel ratio. - Annotation editor with pen, arrow, box, opaque redaction, and text labels; 8-colour palette; undo and clear. Annotations are flattened at the resolution of the original capture, and survive a round-trip through the form's "Edit" button.
- The flow is presented as a route (
ReportFlowRoute) pushed on top of whatever is on screen, so it lands above an open dialog or bottom sheet, text selection and tooltips get theOverlaythey need, and the system back gesture steps back through the reporter (PopScope) instead of popping the app's own screen out from under it. Mounted inMaterialApp.builderthe widget sits above theNavigator, so it locates it by descending from its own element rather than viaNavigator.of(context)(which searches ancestors); passnavigatorKeyto skip the search. With no navigator at all, the flow falls back to anOverlaythe reporter owns.
Verified on device #
Built and driven end-to-end on a Samsung SCG07 (Android 13): capture excludes the button, arrow and
opaque redaction render and flatten, the form reports 12 context rows including Route, the keyboard
leaves Submit reachable, submit attaches a ~149 KB PNG, and back closes the reporter while leaving the
app's own dialog open.
- Auto-captured context: route (with
BugReporterNavigatorObserver), platform, release, device, viewport, screen, text scale, orientation, colour scheme, locale, timezone, and console/network summaries. BugReporterThemewith the shared Indigo / Noir / Mint presets andcopyWithtoken overrides.ClickUpBugReporterRepository, byte-comparable report bodies with the JS core: markdown context block,Recent Logs,Last Failed API Call, screenshot attachment, and best-effort tag attachment after task creation.- Default HTTP backend via
apiUrl, using the same snake_case payload as the JS packages. ReportQueue+ReportQueueStorageoffline retry queue (10-entry cap, drops screenshots rather than reports when storage is full), withInMemoryReportQueueStoragefor tests and demos.BugReporterLogCapturefor breadcrumbs fromFlutterError.onError,PlatformDispatcher.onError, anddebugPrint— chaining to existing handlers so it composes with Crashlytics/Sentry — plusBugReporterHttpClientto record failed requests.