ScreenStack integration
ScreenStack integration
Section titled “ScreenStack integration”ScreenStack is VooStack’s visual-regression product. The FlightStack integration captures Flutter test screenshots in CI, diffs them against your baseline branch, and (optionally) pushes the approved set to App Store Connect and Google Play.
What you get
Section titled “What you get”- Pixel diffs on every PR. Side-by-side view of changed widgets, light vs. dark, every device profile you configure.
- Approval gates. Pull requests can be blocked until visual changes are explicitly approved.
- Store sync. When you’re ready to ship, push the approved set directly to App Store Connect and Play Console — no manual screenshot wrangling.
Setup overview
Section titled “Setup overview”- Create a ScreenStack project and link it to your FlightStack org.
- Instrument your tests with the
screenstackFlutter package. - Add the ScreenStack job to your CI pipelines.
- (Optional) Add the Upload Store Screenshots job to your release pipelines.
1. Link the project
Section titled “1. Link the project”In the FlightStack dashboard, Settings → Integrations → ScreenStack. Authorize the connection, pick the project you want this org to default to. You can override per pipeline.
2. Instrument
Section titled “2. Instrument”import 'package:flutter_test/flutter_test.dart';import 'package:screenstack/screenstack.dart';
import 'package:my_app/login_page.dart';
void main() { testWidgets('Login — empty', (tester) async { await tester.pumpWidget(const MyApp(home: LoginPage())); await ScreenStack.capture(tester, 'login/empty'); });
testWidgets('Login — error', (tester) async { await tester.pumpWidget(const MyApp(home: LoginPage(error: 'bad creds'))); await ScreenStack.capture(tester, 'login/error'); });}ScreenStack.capture() renders the current widget tree at the configured device profile +
theme variant and queues it for upload. The actual upload happens through the job, so unit
tests still run offline.
3. CI pipeline
Section titled “3. CI pipeline”┌──────────┐ ┌────────┐ ┌──────────────┐│ On PR │───▶│ Tests │───▶│ Screen Stack │└──────────┘ └────────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ Notification │ │ (Slack PR) │ └──────────────┘See the ScreenStack job for inputs.
4. Release pipeline
Section titled “4. Release pipeline”┌────────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────────────┐│ On Tag │───▶│ Build iOS │───▶│ Screen Stack │───▶│ Approval ││ v* │ │ │ │ │ │ "ship screenshots?"│└────────────┘ └────────────┘ └──────────────┘ └────────────────────┘ │ ▼ ┌────────────────────┐ │ Upload Store │ │ Screenshots │ └────────────────────┘Device profile recommendations
Section titled “Device profile recommendations”| Family | Profile | Use for |
|---|---|---|
| iPhone 6.7” | iphone-15-pro-max | Required for App Store submissions |
| iPhone 6.5” | iphone-15-pro | Required for older devices |
| iPad 13” | ipad-13 | Required if you target tablet |
| Pixel | pixel-7 | Required for Play Store submissions |
| Android tablet | nexus-tablet | Required if you target Android tablet |
Marketing-quality screenshots usually need a separate, polished pipeline run with mocked data
and explicit pumpAndSettle() calls. Don’t ship debug-mode screenshots to the App Store.
Screens behind a login
Section titled “Screens behind a login”This is the one that bites. If any route in your config requires a signed-in user, you must provide your own capture test. Without it the agent generates a stub that pumps your app with no credentials, every protected route redirects to your sign-in page, and you capture the same screen once per canvas. The job used to report success while doing it.
Create integration_test/screenstack_test.dart. The agent looks for that exact path first
and only falls back to the stub when it is missing.
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';import 'package:integration_test/integration_test.dart';import 'package:screenstack_sdk/screenstack_sdk.dart';import 'package:your_app/screenstack_config.dart';
void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Capture ScreenStack screenshots', (tester) async { // Read from the process environment. Pipeline variables arrive as real // environment variables, NOT as --dart-define, so String.fromEnvironment // reads back empty here. final token = Platform.environment['SCREENSTACK_ACCESS_TOKEN'] ?? ''; if (token.isEmpty) { fail('Set SCREENSTACK_ACCESS_TOKEN on the screen-stack job.'); }
// Everything your main() does before runApp. The SDK pumps a bare app // widget, so config and DI have to be in place first. await AppConfig.initialize(baseUrl: 'https://api.example.com/');
await captureScreenStackScreenshots( tester, binding, screenstackConfig, setup: (tester, context) async { await signInWithToken(token); await tester.pumpAndSettle(); // Assert you actually left the sign-in page before capturing. }, ); });}Three things to get right:
- Bootstrap first.
captureScreenStackScreenshotspumpsconfig.appBuilder()itself. Anything yourmain()normally does beforerunApphas to happen before you call it. - Read credentials from
Platform.environment. NotString.fromEnvironment. - Fail loudly when credentials are missing or stale. A green job that published the wrong artwork is worse than a red one.
Store the token as a pipeline variable on the screen-stack job, not in the job config.
Troubleshooting
Section titled “Troubleshooting”Every screenshot is the sign-in screen
Your screens need auth and you have no integration_test/screenstack_test.dart. See
Screens behind a login. FlightStack blocks the upload when all
captured screenshots are byte-identical, so the listing keeps its previous artwork.
Captures missing on the dashboard
Check the agent logs for screenstack/upload failures. The most common cause is an
expired ScreenStack project token — re-authorize the integration in dashboard settings.
Random diffs from font rendering
Lock the test font with --dart-define=FONT=NotoSans or use Flutter’s loadAppFonts() in
your test harness — system fonts drift between CI agents.
See also
Section titled “See also”- ScreenStack job — job reference
- Upload Store Screenshots job — promote to the stores