flame-harness-admob
Phase 7 — analyze the game, decide a rewarded-ad strategy, guide manual AdMob ad-unit creation, and inject google_mobile_ads + ATT/UMP consent code.
Phase 9 — capture store screenshots in the game's configured locales via integration_test (ads hidden), fill ASO keywords, and upload via fastlane.
$ npx -y skills add tjdrhs90/flutter-flame-harness --skill flame-harness-screenshot --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/flame-harness-screenshotContext preview
The summary Claude sees to decide when to auto-load this skill.
Phase 9 — capture store screenshots in the game's configured locales via integration_test (ads hidden), fill ASO keywords, and upload via fastlane.
name: flame-harness-screenshot description: Phase 9 — capture store screenshots in the game's configured locales via integration_test (ads hidden), fill ASO keywords, and upload via fastlane. argument-hint: "" allowed-tools: [Read, Write, Edit, Bash, Glob]
Phase 9 of the flutter-flame-harness pipeline. Captures KO and EN store screenshots by driving the game's `integration_test` harness on the required device sizes (ads hidden), fills ASO metadata (keywords, localized titles, descriptions), and uploads the screenshots via fastlane.
All file schemas (`config.md`, `state.md`, `pipeline-log.md`) and the phase transition table are defined in `docs/harness-protocol.md` — that document is the single source of truth (§1 for `config.md` and `credentials_dir`; §2 for `state.md`; §6 for log schemas; §7 for the `screenshot → submit` transition and the `status: running` rule). Do not redefine schemas here.
**Prerequisites:** The game has been built and uploaded (Phase 8 `flame-harness-build`). The iOS simulator is a 6.7" iPhone model and the Android emulator is a phone-sized device. `flutter drive` and fastlane must be installed on the developer's macOS machine.
---
Before any action, load:
1. `docs/harness/config.md` — extract `app_slug`, `bundle_id`, `app_name`, and `default_language` (per protocol §1). 2. `docs/harness/state.md` — confirm `next_role: screenshot` (per protocol §2).
Derive the game root path: `<projects-dir>/<app_slug>/`.
---
Copy `templates/screenshots_test.dart.template` into the game's `integration_test/` directory:
mkdir -p <game>/integration_test cp templates/screenshots_test.dart.template \ <game>/integration_test/screenshots_test.dart
Open `<game>/integration_test/screenshots_test.dart` and replace every `// TODO(generator):` comment block with the game's real screen-driving code:
converting hyphens to underscores).
representative UI state (high score, coins, unlocked skins).
screenshots are language-deterministic regardless of the simulator's system locale.
game-over/results, optional secondary screen), keeping zero-padded two-digit name prefixes so fastlane and App Store Connect receive them in order.
Ensure `<game>/test_driver/integration_test.dart` exists. **`integrationDriver()` has no default screenshot writer** — its native path is guarded by `onScreenshot != null`, so without the callback below every `takeScreenshot` byte is silently discarded and no file is ever written. Pass one:
import 'dart:io';
import 'package:integration_test/integration_test_driver_extended.dart';
Future<void> main() => integrationDriver(
onScreenshot: (
String name,
List<int> bytes, [
Map<String, Object?>? args,
]) async {
// The driver runs on the host, so Platform.environment works here (inside
// the test it would not — that side uses String.fromEnvironment).
final dir = Platform.environment['SCREENSHOT_DIR'] ?? 'build/screenshots';
final file = File('$dir/$name.png');
await file.parent.create(recursive: true);
await file.writeAsBytes(bytes);
return true;
},
);---
| Platform | Required device | |---|---| | iOS | 6.7" iPhone simulator (e.g. iPhone 15 Pro Max) | | Android | Phone emulator (e.g. Pixel 7, 1080 × 2400) |
Run `flutter devices` to identify the device ID. Use the `-d` flag to target the correct device.
Pass `--dart-define=screenshots=true` to every `flutter drive` invocation. The game's ad helper must check this flag and suppress all ad units (banner, interstitial, rewarded) during capture so no ad overlays appear in store screenshots. Screenshot mode should also skip the ATT prompt and mute audio (native prompts/sound break automated capture).
**No alpha channel** (App Store rejection): store screenshots — and the iOS app icon — must be flattened to opaque RGB. Check with `sips -g hasAlpha <file>` (must report `hasAlpha: no`).
**`sips -s format png` does NOT strip alpha** — it re-encodes and keeps the channel, so it silently leaves the rejection in place. Use one of these instead:
# Preferred — lossless, needs ImageMagick magick in.png -background white -alpha remove -alpha off out.png # macOS built-ins only — round-trip through JPEG (lossy, fine for screenshots) sips -s format jpeg in.png --out tmp.jpg && sips -s format png tmp.jpg --out out.png sips -g hasAlpha out.png # verify: hasAlpha: no
See `docs/game-gotchas.md` → Store rejections.
// In the ad helper (example):
const bool isScreenshotMode =
bool.fromEnvironment('screenshots', defaultValue: false);Run the capture twice, once per locale (`ko` and `en`), using `--dart-define=SCREENSHOT_LOCALE=`:
`SCREENSHOT_DIR` is read by the `onScreenshot` callback above — give each locale its own directory so the two runs can't overwrite each other:
DEVICE_ID="<ios-simulator-id>" GAME="<absolute-path-to-game-root>" # KO screenshots — iOS SCREENSHOT_DIR=build/screenshots/ios/ko flutter drive \ --driver=test_driver/integration_test.dart \ --target=integration_test/screenshots_test.dart \ -d "$DEVICE_ID" \ --dart-define=SCREENSHOT_LOCALE=ko \ --dart-define=screenshots=true # EN screenshots — iOS SCREENSHOT_DIR=build/screenshots/ios/en-US flutter drive \ --driver=test_driver/integration_test.dart \ --target=integration_test/screenshots_t
A Claude Code plugin that takes a Flutter/Flame game from raw idea all the way to the app stores.
Phase 7 — analyze the game, decide a rewarded-ad strategy, guide manual AdMob ad-unit creation, and inject google_mobile_ads + ATT/UMP consent code.
Phase 8 — bootstrap signing credentials, generate fastlane config from templates, and build + upload signed IPA (TestFlight) and AAB (internal track).
Phase 4 — propose verifiable completion criteria and mandatory hard gates; reach AGREED (1-pass default, multi-round negotiation in --strict).
Phase 3 — define the Flutter design_tokens.dart spec (palette, typography, spacing), the game's art/visual concept, and the asset/audio sourcing plan.
Phase 6 — skeptical QA. Run the game, watch it, then judge against the contract. Default = functional check; --strict adds quality and edge-case passes.
Phase 5 — build the Flame game in 3 gated sub-phases (core loop → systems+components → UI+content), then self-evaluate against the contract.