Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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 pumpAndSettle wait 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

  1. Pin the environment. Pin the Flutter SDK, package versions, emulator or simulator images, and browser version where practical.
  2. Reset and seed state. Clear app data, install the test build, and load deterministic fixtures.
  3. Launch the target. Start the selected emulator, simulator, browser, or hosted device before invoking the test.
  4. Exercise the screen. Navigate by semantics or stable keys, wait for required data, settle animations, and call takeScreenshot.
  5. Collect artifacts. Save PNG bytes through onScreenshot; preserve logs and a device description beside each image.
  6. Compare where needed. Run golden comparisons for regression gates, using approved baselines for the exact device and configuration.
  7. 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.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For Flutter documentation, landing pages, or other browser-rendered assets, call the API as documented at https://screenshotneo.com/docs/:

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_test for screenshots of the app on Android, iOS, or Web.
  • Use golden_screenshot when 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.