perfect_flutter 0.1.34
perfect_flutter: ^0.1.34 copied to clipboard
DevTools-based pixel-perfect overlay for Flutter. Inject a design image overlay onto a running debug app from the DevTools panel — no widget wrapping, no app code changes.
perfect_flutter #
DevTools-based pixel-perfect overlay for Flutter. Overlay a design image on top of your running app — emulator, simulator, or physical device — from the DevTools panel. No widget wrapping. No conditional debug paths.
A dev_dependencies entry plus one import line is all the consumer app code
this tool requires. In release builds, tree-shaking removes everything.
Install #
-
Add to your app's
pubspec.yaml:dev_dependencies: perfect_flutter: ^0.1.34 -
Add one import at the top of
lib/main.dart(anywhere in your app works, but the top ofmain.dartis conventional):// ignore: unused_import, depend_on_referenced_packages import 'package:perfect_flutter/perfect_flutter.dart';The import has no runtime effect — it only ensures the runtime helper class is linked into the debug build so the DevTools panel can call it via the VM service. Do not let your IDE auto-remove this import (e.g. via "Organize Imports" or
dart fix) — it will silently break the tool. The// ignore: ...comment suppresses the "unused import" lint. -
flutter pub get -
Run the app in debug mode (
flutter run, orF5in your IDE). -
Open DevTools → click the Perfect Flutter tab.
-
Upload a design image → the overlay renders on the device, centered and half-opaque by default. Adjust opacity, offset, scale, and flip from the panel.
No widget wrapping. No runApp changes. No conditional debug branches.
Features #
Inject an overlay and upload a design #
Click Inject in the panel → a magenta placeholder renders on the device. Then pick a PNG / JPG — the upload streams in 256 KB chunks with a progress bar and the overlay replaces the placeholder at 50% opacity, centered.

Follow scroll #
Opt-in toggle (off by default). The overlay translates with the app's currently-visible vertical scrollable. Picks the right scrollable per-frame by visible area, so route changes and nested inner lists handle cleanly.

Opacity #
Slider from 0 to 1.

Offset, scale, flip #
Numeric rows with ±1 px nudges for offset; multiplicative ±5% per click for scale (range 0.01–100); toggle chips for flip H / V.

Hot-restart resilience #
Image bytes + transforms persist to localStorage and auto-restore against
the new isolate within ~2s. Manual Restore button as fallback if the
DevTools iframe remounts before the auto-trigger fires.
Hot reload from panel #
The ⚡ app-bar button calls service.reloadSources; falls back to a
"press r in your terminal" message when the kernel task is owned by
flutter run.
Keyboard shortcuts #
Arrows = ±1 px offset, Shift+Arrows = ±10 px, [ / ] = opacity ±5%,
- / = = scale ±5%, h / v = flip, space = show/hide. Discoverable
via the keyboard icon in the app bar.
Touch passthrough #
Taps, scrolls, drags, and gestures all pass through to the app — the
overlay sits behind an IgnorePointer. You can interact with your app
normally while comparing against the design.
FAQ #
Does perfect_flutter ship in release builds? #
No. Two layers of defense:
- Tree-shaking. Nothing in your app calls
PerfectFlutter.*— the class and everything it references are unreachable frommain(), so Flutter's release builder strips them. - VM service is debug-only. Even if the runtime code somehow survived tree-shaking, the DevTools extension would have nothing to talk to.
Adding to dev_dependencies is belt + braces.
Why do I need the import line? #
Dart's VM debug expression evaluator only accepts arrow-bodied function
literals — block bodies and IIFEs fail to parse. That makes a true
"zero app code" architecture infeasible. The import is the one-line cost
of having a normal Dart helper class that the panel can call short
expressions against (PerfectFlutter.inject(), setOpacity(0.5), etc.)
instead of stuffing every feature into a single arrow expression.
My IDE keeps removing the import. #
Use the // ignore: unused_import, depend_on_referenced_packages comment
shown above. Most "Organize Imports" actions respect it. If yours doesn't,
disable that action for the file containing the import, or reference the
PerfectFlutter symbol once (e.g. // ignore: unused_local_variable final _ = PerfectFlutter;) to mark it used.
The DevTools tab doesn't appear. #
- Confirm
perfect_flutteris indev_dependenciesandflutter pub gethas run. - Reload the DevTools page. The extension is discovered via
.dart_tool/package_config.json, which is rewritten bypub get. - Some setups need a full DevTools restart after the first
pub get.
The Inject button shows "perfect_flutter runtime not loaded". #
The consumer app is missing the package:perfect_flutter/perfect_flutter.dart
import (or your IDE removed it). Add it back.
The overlay is clipped at the bottom on long screens. #
The overlay is bounded by the viewport. Toggle on Follow scroll in the Display section so the overlay translates with content as you scroll.
Can I overlay multiple images at once? #
No — single overlay only. Multi-layer was considered and scoped out as feature creep; the pixel-perfect comparison flow works fine with one design at a time.
Hot restart loses the overlay. #
It re-injects automatically. On R in flutter run, the panel waits ~2s
for the new isolate to finish runApp(), then re-injects + re-uploads
the cached image + replays transforms. If the DevTools iframe remounts
before the auto-trigger fires, a Restore button appears next to
Inject — click it.
Compatibility #
- Flutter 3.10+
- Debug builds only (VM service is required)
- Verified end-to-end on
MaterialAppon physical Android.CupertinoAppand custom-root apps should work but are not yet exhaustively tested.
Links #
- Source: https://github.com/Hankium/perfect-flutter
- Issues: https://github.com/Hankium/perfect-flutter/issues
License #
MIT — see LICENSE.