---
title: Checking thirty Flutter screens against Figma without opening the app
date: 2026-09-23
tags: [flutter, figma, adb, design-review, screenshots, golden-tests]
type: success
status: n/a
reproducible: true
stack: Flutter / Dart, Android (adb), Figma
agent: Claude Code
model: claude-fable-5-1
---

# Checking thirty Flutter screens against Figma without opening the app

I had a Flutter app with about thirty screens built from a Figma file and one question I could not answer quickly: does every screen actually look like the design? Golden tests lock the current rendering, but they don't tell you whether that rendering matches Figma. Clicking through the whole application flow on a phone every time something changed was the alternative, and I stopped doing it.

This is a one-time job, not a routine. The comparison is needed once, when the app is first built from the design, to confirm that what the tests will guard is actually right. After that pass the golden tests do the work: every approved screen is locked as an image, and any later change that moves a pixel fails the test suite before it reaches a device. So the story is really about preparing the goldens to be trustworthy.

I asked my agent for a first-run routine: take the original frames from Figma, take the same screens from the real device, and produce one image per screen with the design on the left and the app on the right, so I can review them at my own pace.

What we ended up with is small: a JSON manifest (screen name, Figma node id, fallback golden image, title), a script that downloads the Figma frames at 1x through the REST API (the agent can also export them through the Figma MCP, and screens designed in a Claude Design artifact drop into the same folder), an adb capture script, and a Dart compositor using the `image` package that scales both sides to the design width, writes a sheet per screen, and an HTML index to scroll through.

The friction was almost all on the phone side. The agent drove the app over adb to reach each screen, and three things bit repeatedly: `adb shell input text` drops spaces unless you write `%s`; the Back key pops the current route when the keyboard is already hidden, so "press Back to dismiss the keyboard" silently threw us out of a form; and a tap during a cold-start jank registers as a long press. The first comparison run also confused me: some sheets showed a back button and lighter text, others didn't. That was the fallback in action. Golden test images are rendered at 1x without a navigator, so they look heavier and lack the back chevron; device captures don't. The sheet header now names the source.

The routine paid for itself on the first pass. Two layout bugs were obvious side by side: a decorative corner ribbon covering a status badge, and a card with a hard shadow band under its labels. A third bug hid in the mock backend, a raw-string regex ending in `\$` that rejected every ID. Once those were fixed and the sheets approved, the goldens were re-recorded and the review moved into the test suite for good.

## Reproduce this
Prerequisites: a Flutter app with screens designed in Figma (or a Claude Design artifact) and golden tests per screen; Android device with USB debugging, `adb` on PATH; the `image` Dart package (https://pub.dev/packages/image) as a dependency; a Figma personal access token, or an agent with the Figma MCP (https://help.figma.com/hc/en-us/articles/32132100833559) to export frames.
Your agent will need from you: the Figma file key and one node id per screen; a way to reach every screen in the app (a mock backend or seeded state); the width of the design frames (375 px for a standard mobile frame).
Steps:
1. Create `tool/screen_compare/screens.json` listing each screen: `name`, `figma` node id, optional `golden` fallback image, `title`.
2. Export every listed frame at 1x into `tool/screen_compare/design/<name>.png` — via `GET https://api.figma.com/v1/images/{fileKey}?ids={node}&format=png&scale=1` with the `X-Figma-Token` header, or via the Figma MCP `get_screenshot` tool; Claude Design screens are exported as PNG into the same folder.
3. Add `capture.sh <name>` that runs `adb exec-out screencap -p > device/<name>.png`. Navigate the app to each screen (manually or via adb input) and capture it. Avoid the Back key to hide the keyboard; check `adb shell dumpsys input_method | grep mInputShown` first. Use `%s` for spaces in `adb shell input text`.
4. Write `compare.dart` with `package:image`: decode the design PNG and the device PNG (fall back to the golden), `copyResize` both to the design width, composite them side by side on a canvas with a labelled header naming the source, encode to `out/<name>.png`, and emit an `index.html` with one section per screen.
5. Run `dart run tool/screen_compare/compare.dart` and open `out/index.html`; review each sheet, fix the app, re-capture only the screens that changed.
6. When every sheet is approved, re-record the golden tests (`flutter test --update-goldens`) so the approved rendering is what the suite guards from now on; the comparison is not repeated unless the design changes.
Verify: `out/` contains one PNG per manifest entry with a design image present, each exactly twice the design width plus gutters; the HTML index lists every screen; a deliberate 20 px layout change in one screen is visible in that screen's sheet after re-capturing and re-running the compositor, and the same change fails the corresponding golden test.
