decocms/studio/flutter-app
Claude Code and agent skill by decocms/studio
- community
Checking that the SKILL.md file is still in the repository.
Work on a Flutter/Dart repository (one with a pubspec.yaml) in the sandbox — resolve private pub dependencies, run analyze/test, and QA the UI on an Android emulator or by compiling to web, and screenshot it. Use whenever the repo you are working in is a Flutter app, and especially before reporting that you cannot see or exercise a mobile app's UI.
Install
npx vskill@latest install decocms/studio/flutter-appSKILL.md
flutter-app — building and QA'ing an app that is not a website
A Flutter repo breaks an assumption the rest of the sandbox is built on: its pull requests have no deploy previewUrl, so the usual "open the preview and look at it" does not apply — and neither does giving up.
First: check which sandbox image you are on
The Flutter SDK ships only in the Android sandbox image. If qa-android is on your PATH (equivalently, $STUDIO_SANDBOX_ANDROID_EMULATOR is set), you are on it: the SDK is at /opt/flutter (already on PATH), and there is an Android emulator to run the app for real — see Running the app on the emulator.
Otherwise you are on the default image, which has no Flutter SDK: no flutter analyze, no flutter test, no way to see the UI. Do not try to install the SDK into the sandbox. Do what reading the code can do, then say in your report that you could not build or run the app and that the repo must be switched to the Android emulator image in Settings → Repositories. That is the fix; do not spend the run working around it.
Check the version pin first
flutter analyze produces different findings on different releases. Compare the image's flutter --version against whatever the repo's CI pins (commonly FLUTTER_VERSION in .github/workflows/*.yml, or .fvmrc). If they differ, say so in your report and treat analyzer findings that the repo's own gate would not produce as suspect. Do not "fix" them silently.
Private dependencies
Most real apps pull packages from private git repos:
flutter pub get fetches those over plain HTTPS, and the sandbox is already set up for it: the daemon installs the run's credentials in the pod's git config at boot, so flutter pub get needs no preparation. Just run it.
Never run `gh auth setup-git` here. The first thing it writes is an emptycredential.<url>.helper, which git reads as "discard every helper configuredearlier" — and becauseGIT_CONFIG_GLOBALpoints at the daemon's config, thatwrite lands in the same file and deletes the credentials the pod booted with.Apub getthat worked will start 404ing.
What the credentials reach is decided before the run starts: Studio walks the git: dependencies in pubspec.yaml, and the dependencies of those, and mints the token for this repository plus every same-owner repository in that graph (capped at 20), plus any git credential the organization configured for the host. So a normal private dependency tree resolves, and three cases do not:
- A repository outside the token and with no configured credential. The
minted token belongs to one GitHub App installation and covers only the repositories that installation was granted, so a dependency under a different owner — or one the installation was never given — is out of reach. The fix is not yours to apply: someone adds a git credential for that host in Settings → Repositories (a PAT stored as an org secret), and the next boot installs it for every git in the pod, pub get included. Report that as the blocker and name the host; do not go looking for a token yourself.
- A private dependency added on your working branch. The walk read each
repository's default branch, so a git: dependency your PR introduces is not in the token. Say so in your report rather than working around it.
- A repository the organization did not authorize for Studio. The walk stops
at the grant, by design.
So a 404 from pub get is one of those three — report it. Do not go looking for another token, and never paste one into pubspec.yaml, a git URL, or a config file.
flutter pub get on a cold sandbox takes a few minutes. Run it once.
The checks
Run these before you hand over. They are fast and they are what CI will say.
Running the app on the emulator (qa-android)
Only on the Android sandbox image. This builds the app's Android target, boots a headless emulator, installs the app and launches its real main() — native plugins included, so Firebase, push and the rest behave as on a phone.
Then Read the PNGs. A screenshot you never opened is not verification. To show them to a person, embed each as a markdown image in your comment —  renders on the task; a bare path or a code span does not.
Run `qa-android start` in the foreground, with the Bash tool's longest timeout (600000 ms). It returns within about nine minutes: exit 0 means the app is up, 1 that it failed (the reason is printed), and 2 that the build is still going — run qa-android wait to keep waiting, as many times as it takes. Never background it and end your turn waiting for a notification — in an autonomous run, ending the turn ends the run.
Find things with `ui`, then tap them. It lists every element Android's accessibility layer sees — for a Flutter app, its Semantics tree: text, button labels, tooltips — with the centre point of each. Tapping a label's coordinates beats estimating a position from the picture. Coordinates are screen pixels, and the screen is 720x1600, the same as the screenshots, so a point read off a PNG works too. An icon-only button with no tooltip or semantic label will not appear in ui; use the screenshot for those.
The first `start` is slow — Gradle downloads its dependencies and compiles the app, several minutes for a real one. Later starts reuse the Gradle cache and resume the emulator from a snapshot. Do not stop and restart to "refresh": start again rebuilds and relaunches.
Run nothing heavy alongside `qa-android`. No flutter build web, flutter test or other flutter build while start runs or the emulator is up — qa-android stop first. Try qa-android before the web build, not after.
If `start` says `/dev/kvm` is not available, the sandbox landed on a node without KVM. That is an infrastructure problem, not yours — report it.
What the emulator does not have: a signed-in Google account, a camera, biometrics, or real push delivery. Screens behind those, and flows that need a real payment or an SMS code, are the ones to name as not exercised.
Looking at the UI (web build)
A fallback for when qa-android start fails for a reason outside the change (a Gradle error naming a missing SDK package, say — report that too). Prefer qa-android whenever it works: the web build cannot run native plugins.
Then Read the PNG. A screenshot you never opened is not verification.
--flutter matters. Flutter paints the whole app into a single <canvas>: the DOM contains no text and no widgets, so without it you would be clicking blind pixels. The flag turns on Flutter's accessibility tree, which materializes one <flt-semantics> element per widget, and prints them:
That listing is your map of the screen. It is also an assertion target: if the widget you just added is not in it, it did not render — but read the count first. A busy screen prints only the first 60 and says so; --label still matches against all of them, so ask for yours by name before concluding it is missing.
Frame one widget for a focused before/after — the equivalent of --selector on a web page:
Do NOT reach for --selector here. Flutter emits no `aria-label`, and element ids are a per-build counter (flt-semantic-node-8), so there is no stable CSS handle — the label is the handle. --label matches the exact text first, then a unique case-insensitive substring, and tells you the candidates if it is ambiguous.
To drive the app, aim the mouse at a box centre. A semantics node is a transparent overlay, so page.click(selector) fails Playwright's visibility checks — coordinates are the way in:
Then re-read the semantics tree to assert what changed — it is the only readable representation of app state a canvas app has.
One wrinkle when you re-read it in the same page: interacting with a widget can add a SECOND node carrying the same label — a tapped FloatingActionButton materializes its tooltip, so "Increment" appears twice from the first click on. Match on the tappable node or the one whose box you clicked, not on "the only node with this label". A fresh qa-screenshot run reloads the page, so --label is unaffected.
When the web build fails
It usually fails for one of two reasons, and both are worth reporting rather than working around:
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingpackages/sandbox/image/skills/flutter-app/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/decocms/studio/flutter-app)