The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →The official way to automate screenshots in Flutter is integration_test. Launch your app in a test, wait for a stable frame with pumpAndSettle(), convert the Android surface when required, and call takeScreenshot(). A host-side driver receives PNG bytes that you can save as CI artifacts, compare with goldens, or upload for release workflows.
Choose the right Flutter screenshot method
Your goal determines the correct layer. Flutter screenshot automation is not one single feature: a widget golden, a device capture, and an App Store asset have different requirements.
| Goal | Best fit | What you gain | Trade-off |
|---|---|---|---|
| Check a widget or screen against a visual baseline | Flutter golden test | Fast, repeatable widget-level comparison | It does not exercise a real device’s system rendering. |
| Capture the app as rendered on Android, iOS, or the Web | integration_test |
Uses the target runtime and produces screenshot bytes | Requires a device, emulator, simulator, or browser target. |
| Create framed, multi-device store artwork | golden_screenshot |
Device profiles, custom devices, frames, and store-oriented output | Adds package configuration and generated golden files. |
| Run a device-model matrix | integration_test with Firebase Test Lab |
Coverage across many hosted devices | More infrastructure and execution cost than a local emulator. |
Set up an integration screenshot test
1. Add the test dependencies
In pubspec.yaml, put both packages under dev_dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
Run flutter pub get. Keep the Flutter SDK and test dependencies pinned in CI so a dependency update does not silently change rendering or test behavior.
Recommended Free Tools
#1 Best Overall
2. Create a capture test
Create a file such as integration_test/screenshots_test.dart. This complete example starts the app, performs the Android surface conversion, waits for settled frames, and captures a named image:
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('capture home screen', (tester) async {
app.main();
// Required for the documented Android screenshot flow.
await binding.convertFlutterSurfaceToImage();
// Wait for startup work, animations, and scheduled frames to settle.
await tester.pumpAndSettle();
await binding.takeScreenshot('home');
});
}
Replace my_app with your package import. The string passed to takeScreenshot is the artifact name; use stable names such as home-light-en-us rather than timestamps so CI output and baselines map predictably.
3. Run against the target
Start the Android emulator, iOS simulator, connected device, or browser target you intend to capture, then run the integration-test command used by your Flutter version. Flutter’s documented driver pattern is:
flutter drive
--driver=test_driver/integration_test.dart
--target=integration_test/screenshots_test.dart
The exact runner arrangement can vary with the Flutter release, but the test itself remains an integration_test test. For published assets, run the same test on every device profile, orientation, locale, and theme you support.
Save PNG screenshots on the host
takeScreenshot transfers a screenshot to the host-side driver as PNG bytes. The extended integration driver exposes a callback where you can write those bytes to disk or upload them to an artifact store.
Rank #2
import 'dart:io';
import 'package:integration_test/integration_test_driver_extended.dart';
Future<void> main() async {
await integrationDriver(
onScreenshot: (name, bytes, [args]) async {
File('$name.png').writeAsBytesSync(bytes);
return true;
},
);
}
The callback receives the screenshot name, a PNG byte buffer, and optional JSON-serializable arguments. Because it executes on the host, it can read CI environment variables, create directories, attach metadata, or send the bytes to your build’s artifact service.
Use a driver file such as test_driver/integration_test.dart for the callback, then run the driver with the target test. Ensure the CI workspace is writable and that artifact collection runs even when a later test fails.
Make captures deterministic
Wait for visual stability
- Call
pumpAndSettle()after navigation and after data needed by the screen has loaded. - Disable, shorten, or explicitly await animations that never settle. An indeterminate progress animation can make
pumpAndSettlewait indefinitely. - Wait for a known widget or state before capturing when startup includes asynchronous work; settling frames alone does not prove that a network response has arrived.
Control app state
- Reset storage between runs and seed deterministic records.
- Use fixed clocks, locales, feature flags, and test accounts where your app displays time, prices, or personalized content.
- Stub network calls or provide a predictable test backend. A production response can change the pixels while the test is running.
Keep names and dimensions intentional
Name captures with the screen, device profile, orientation, locale, and theme when those dimensions matter. Keep emulator and simulator configuration under source control. A screenshot from a different pixel ratio or text scale is not a valid comparison even if the logical layout is identical.
Android, iOS, and Web differences
Android
Call convertFlutterSurfaceToImage() before pumping and capturing, as shown above. Omitting this Android-specific conversion can prevent the expected image from being captured. Invoke it before the first settled frame that you want to save.
iOS
Run the same integration test on a simulator or physical device selected for your release matrix. Keep the simulator’s scale, appearance, orientation, and status-bar configuration consistent across runs.
Web
The integration-test flow also supports a browser target. Select the browser and viewport deliberately, wait for web fonts and asynchronous content, and avoid comparing a browser capture with a mobile capture as though they were the same rendering environment.
Golden tests versus device screenshots
Use a normal Flutter golden when the question is “did this widget change from its approved baseline?” Goldens are efficient for component and screen regression tests, but they are not a substitute for checking system rendering on a real Android or iOS runtime.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesUse integration_test when you need the pixels produced by the target runtime, including device text rendering, platform behavior, navigation, permissions, and real app startup. You can combine both: golden tests catch small widget changes quickly, while a smaller integration suite validates release-critical screens.
When a golden is intentionally changed, regenerate it with:
flutter test --update-goldens
When golden comparisons run through integration_test on Android or iOS, Flutter’s documented default comparator proxies to the host filesystem unless you configure a custom comparator. This avoids the earlier device-path problem; keep any custom comparator only when your workflow needs different storage or comparison behavior.
Rank #4
Generate framed App Store screenshots
Raw device captures are usually not the final store deliverable. The golden_screenshot package adds common device profiles, custom devices, frames, and store-oriented output to a golden-style workflow. Configure the profiles you publish, generate the images, and review text legibility and safe areas at the store’s required dimensions.
For a large catalog, keep the source capture and the framed derivative as separate artifacts. That lets you regenerate frames without rerunning every device test and makes it clear whether a visual difference came from the app or from presentation.
Run screenshot automation in CI
- Pin the environment. Pin the Flutter SDK, package versions, emulator or simulator images, and browser version where practical.
- Reset and seed state. Clear app data, install the test build, and load deterministic fixtures.
- Launch the target. Start the selected emulator, simulator, browser, or hosted device before invoking the test.
- Exercise the screen. Navigate by semantics or stable keys, wait for required data, settle animations, and call
takeScreenshot. - Collect artifacts. Save PNG bytes through
onScreenshot; preserve logs and a device description beside each image. - Compare where needed. Run golden comparisons for regression gates, using approved baselines for the exact device and configuration.
- Repeat the matrix. Run the same named captures for each locale, theme, orientation, and device profile you actually publish.
For broad device coverage, Flutter’s integration-testing guidance identifies Firebase Test Lab as an option. Treat it as a separate matrix job: local tests provide fast feedback, while hosted devices expand compatibility coverage.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| No image or an unexpected Android image | The Flutter surface was not converted. | Call await binding.convertFlutterSurfaceToImage() before pumping and capturing. |
| Capture contains a spinner or half-loaded page | The test captured before asynchronous work finished. | Wait for a known loaded state, then call pumpAndSettle(). |
pumpAndSettle never returns |
An indeterminate animation or recurring frame is active. | Disable it in the test build or replace it with an explicit bounded wait. |
| Flaky pixel differences | Live network data, time, random values, fonts, device scale, or animations vary. | Seed data, fix the clock and configuration, bundle or await fonts, and pin the device environment. |
| PNG files are missing in CI | The host callback is not used, the workspace is unwritable, or artifacts are not collected after failure. | Use integration_test_driver_extended.dart, create the output directory, check write permissions, and configure always-run artifact collection. |
| Only one device’s screenshots are produced | The job ran one target instead of a matrix. | Parameterize the target device, locale, theme, and orientation and run each combination deliberately. |
| Golden comparison fails after a Flutter upgrade | Rendering or dependency behavior changed. | Review the diff on the pinned target, decide whether it is an intentional change, then update goldens deliberately with flutter test --update-goldens. |
Or skip the browser setup
If you need screenshots of web pages rather than Flutter’s in-app integration captures, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off.
Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
For Flutter documentation, landing pages, or other browser-rendered assets, call the API as documented at https://screenshotneo.com/docs/:
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
It also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL-based caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work for easier migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is on every plan. Sign up for the free plan.
Practical decision checklist
- Choose a golden test for fast widget-level visual regression.
- Choose
integration_testfor screenshots of the app on Android, iOS, or Web. - Use
golden_screenshotwhen you need framed, store-oriented device assets. - Add a hosted device matrix when local targets cannot represent your support range.
- Make state, timing, fonts, viewport, and artifact naming deterministic before diagnosing pixel diffs.
- Keep raw captures, comparisons, and framed store outputs as separate pipeline artifacts.
Frequently Asked Questions
Can I capture only one widget instead of the whole Flutter screen?
The documented integration_test screenshot flow captures the rendered target surface. For widget-level output, use a Flutter golden test; for a framed or processed derivative, generate it from the saved PNG in a separate step.
Free tools Windows power users keep installed
One-click scans. No signup required.
Does takeScreenshot return a Dart image object?
No. The integration driver receives PNG bytes through its host-side screenshot callback, where you can write a file or upload the bytes.
Should screenshot tests run on every pull request?
Run a small deterministic smoke set on pull requests and the full locale, theme, orientation, and device matrix on a scheduled or release workflow when runtime time is limited.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

