screenstack_sdk 2.0.2 copy "screenstack_sdk: ^2.0.2" to clipboard
screenstack_sdk: ^2.0.2 copied to clipboard

Automated screenshot capture for Flutter apps. Configure routes, capture screens during CI/CD builds, and upload to ScreenStack device frames for app store assets.

ScreenStack SDK #

Automated screenshot capture for Flutter apps. Capture app screens during CI/CD builds and upload them to ScreenStack device frames for beautiful app store screenshots.

pub package License: MIT

Features #

  • Automated capture: Navigate to routes and capture screenshots automatically
  • Multi-canvas support: Upload to iOS, Android, and custom canvases simultaneously
  • Type-safe config: Dart-first configuration with full IDE support
  • Manifest generation: JSON manifest for FlightStack agent integration
  • Validation: Catch config errors before running expensive CI builds

Installation #

Add to your pubspec.yaml:

dev_dependencies:
  screenstack_sdk: ^1.0.0

Quick Start (Simple Mode) #

1. Create Configuration #

Create lib/screenstack_config.dart:

import 'package:screenstack_sdk/screenstack_sdk.dart';
import 'main.dart';

// Simple mode - route-to-frame mapping configured in FlightStack UI
final screenStackConfig = ScreenStackConfig(
  appBuilder: () => const MyApp(),
  routes: {
    '/': 'home_screen',
    '/login': 'login_screen',
    '/settings': 'settings_screen',
  },
);

2. Create Integration Test #

Create integration_test/screenshot_test.dart:

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:screenstack_sdk/screenstack_sdk.dart';
import 'package:my_app/screenstack_config.dart';

void main() {
  final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Capture ScreenStack screenshots', (tester) async {
    final result = await captureScreenStackScreenshots(
      tester,
      binding,
      screenStackConfig,
    );

    expect(result.allSuccessful, isTrue);
  });
}

3. Configure FlightStack Pipeline #

Add a ScreenStack job to your FlightStack pipeline with upload mode enabled.


Advanced Mode (Multi-Canvas) #

For projects targeting multiple app stores (iOS + Android) or multiple locales, use advanced mode to define exactly where each screenshot goes:

import 'package:screenstack_sdk/screenstack_sdk.dart';
import 'main.dart';

final screenStackConfig = ScreenStackConfig.advanced(
  appBuilder: () => const MyApp(),
  screens: [
    // Home screen → uploads to both iOS and Android canvases
    Screen(
      route: '/home',
      name: 'home',
      targets: [
        FrameTarget(canvas: 'iOS App Store', view: 'Home'),
        FrameTarget(canvas: 'Google Play', view: 'Home'),
      ],
    ),

    // Login screen → uploads to both canvases
    Screen(
      route: '/login',
      name: 'login',
      targets: [
        FrameTarget(canvas: 'iOS App Store', view: 'Login'),
        FrameTarget(canvas: 'Google Play', view: 'Login'),
      ],
    ),

    // Settings with custom settle delay for animations
    Screen(
      route: '/settings',
      name: 'settings',
      settleDelay: Duration(seconds: 1),
      targets: [
        FrameTarget(canvas: 'iOS App Store', view: 'Settings'),
        FrameTarget(canvas: 'Google Play', view: 'Settings'),
      ],
    ),
  ],
);

String Path Syntax #

For more concise definitions, use Screen.withPaths:

Screen.withPaths(
  route: '/home',
  targetPaths: [
    'iOS App Store/Home',  // canvas/view format
    'Google Play/Home',
  ],
)

ScreenStack Hierarchy #

Understanding the ScreenStack structure helps with configuration:

Project (e.g., "My App")
├── Canvas: "iOS App Store"
│   ├── View: "Home"
│   │   ├── Device Frame: iPhone 15 Pro
│   │   ├── Device Frame: iPhone 15 Plus
│   │   └── Device Frame: iPad Pro
│   ├── View: "Login"
│   └── View: "Settings"
│
└── Canvas: "Google Play"
    ├── View: "Home"
    │   ├── Device Frame: Pixel 8
    │   └── Device Frame: Pixel Tablet
    ├── View: "Login"
    └── View: "Settings"

Key insight: When you target FrameTarget(canvas: 'iOS App Store', view: 'Home'), the screenshot uploads to all device frames within that view. This means one screenshot automatically fills iPhone 15 Pro, iPhone 15 Plus, and iPad Pro frames.


Workflow #

┌─────────────────────────────────────────────────────────────────┐
│                         FlightStack CI/CD                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. CAPTURE                    2. UPLOAD                         │
│  ┌──────────────┐             ┌──────────────┐                  │
│  │ Run Flutter  │             │  Upload to   │                  │
│  │ integration  │────────────▶│  ScreenStack │                  │
│  │    test      │             │ device frames│                  │
│  └──────────────┘             └──────────────┘                  │
│         │                            │                           │
│         ▼                            ▼                           │
│  ┌──────────────┐             ┌──────────────┐                  │
│  │ Screenshots  │             │  ScreenStack │                  │
│  │   + manifest │             │  composites  │                  │
│  └──────────────┘             └──────────────┘                  │
│                                      │                           │
│                                      ▼                           │
│                              3. EXPORT/PUBLISH                   │
│                              ┌──────────────┐                   │
│                              │  App Store   │                   │
│                              │  Google Play │                   │
│                              └──────────────┘                   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

API Reference #

ScreenStackConfig #

Main configuration class.

Property Type Description
appBuilder Widget Function() Returns your app's root widget
routes Map<String, String>? Simple mode: route → screenshot name
screens List<Screen>? Advanced mode: screens with targets
settleDelay Duration Default delay after navigation (500ms)
projectId String? Optional ScreenStack project ID

Constructors:

  • ScreenStackConfig() - Simple mode with route mapping
  • ScreenStackConfig.advanced() - Advanced mode with explicit targets

Screen #

Defines a screen to capture and where to upload it.

Property Type Description
route String Route path to navigate to
name String? Screenshot filename (auto-generated if null)
targets List<FrameTarget> Upload destinations
settleDelay Duration? Override global settle delay

Constructors:

  • Screen() - Standard constructor
  • Screen.withPaths() - Convenience constructor using string paths

FrameTarget #

Specifies where to upload a screenshot.

Property Type Description
canvas String Canvas name in ScreenStack
view String View name within canvas

Constructors:

  • FrameTarget() - Standard constructor
  • FrameTarget.parse() - Parse from "canvas/view" string

CaptureSessionResult #

Returned from captureScreenStackScreenshots().

Property Type Description
results List<CaptureResult> Results per screen
allSuccessful bool True if all captures succeeded
successCount int Number of successful captures
failureCount int Number of failed captures

Methods:

  • writeManifest() - Write JSON manifest for agent

Manifest Format #

The SDK generates manifest.json for the FlightStack agent:

{
  "outputDir": "build/screenstack_screenshots",
  "capturedAt": "2024-01-15T10:30:00.000Z",
  "totalScreens": 3,
  "successful": 3,
  "failed": 0,
  "screens": [
    {
      "route": "/home",
      "name": "home",
      "filePath": "build/screenstack_screenshots/home.png",
      "success": true,
      "targets": ["iOS App Store/Home", "Google Play/Home"]
    }
  ]
}

Validation #

Validate your config before running CI:

final errors = screenStackConfig.validate();
if (errors.isNotEmpty) {
  print('Configuration errors:');
  for (final error in errors) {
    print('  - $error');
  }
}

Handling Authentication #

Most apps require login to access certain screens. Here's how to handle it:

Use the setup callback to authenticate before screenshots:

testWidgets('Capture screenshots', (tester) async {
  await captureScreenStackScreenshots(
    tester,
    binding,
    screenStackConfig,
    setup: (tester, context) async {
      // YOUR AUTH LOGIC - examples:

      // Firebase Auth
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: Platform.environment['TEST_EMAIL']!,
        password: Platform.environment['TEST_PASSWORD']!,
      );

      // Supabase
      await Supabase.instance.client.auth.signInWithPassword(
        email: 'test@example.com',
        password: 'testpass',
      );

      // Custom auth service
      await AuthService.signIn(testCredentials);

      await tester.pumpAndSettle();
    },
    teardown: (tester, context) async {
      await AuthService.signOut();
    },
  );
});

Option 2: Demo/Screenshot Mode #

Detect screenshot capture and bypass auth entirely:

// In your app's initialization
void main() {
  final isScreenshotMode = Platform.environment['SCREENSTACK_CAPTURE'] == 'true';

  runApp(MyApp(
    // Use mock data for screenshots
    authOverride: isScreenshotMode ? MockAuthProvider() : null,
  ));
}

This gives you perfect control over screenshot content.

Option 3: Test-Specific Routes #

Create unauthenticated versions of screens for testing:

// In your router
GoRoute(
  path: '/preview/dashboard',  // No auth required
  builder: (context, state) => DashboardPage(
    user: DemoUser(),
    data: DemoData(),
  ),
),

Then capture /preview/dashboard instead of /dashboard.

Environment Variables #

For secrets, use your CI/CD pipeline's secrets feature:

  • GitHub Actions: Repository secrets
  • FlightStack: Pipeline environment variables
  • GitLab CI: CI/CD variables

The agent passes all environment variables to your integration test.


Best Practices #

1. Match naming conventions #

Use consistent naming between routes and views:

  • Route /home → View Home
  • Route /settings → View Settings

2. Handle async data loading #

Set appropriate settleDelay for screens that fetch data:

Screen(
  route: '/dashboard',
  settleDelay: Duration(seconds: 2), // Wait for API calls
  targets: [...],
)

3. Test locally first #

Run the integration test locally before pushing:

flutter test integration_test/screenshot_test.dart

4. Keep routes simple #

Avoid routes requiring specific IDs or authentication state. For authenticated screens, set up mock auth in your test:

testWidgets('Capture screenshots', (tester) async {
  // Set up mock authentication
  await MockAuthService.signInAsTestUser();

  final result = await captureScreenStackScreenshots(...);
});

5. Use const for better performance #

const screenStackConfig = ScreenStackConfig(
  appBuilder: MyApp.new,
  routes: {
    '/': 'home',
  },
);

Troubleshooting #

"Route not found" errors #

Ensure routes are registered in your router:

  • go_router: Check GoRoute path definitions
  • Navigator: Check onGenerateRoute or named routes map

Screenshots are blank or incomplete #

Increase settleDelay to allow widgets to fully build:

Screen(
  route: '/home',
  settleDelay: Duration(seconds: 2),
  targets: [...],
)

The SDK tries go_router first, then falls back to Navigator. Ensure your app uses standard navigation:

// go_router - SDK calls this:
GoRouter.of(context).go('/home');

// Navigator - SDK falls back to:
Navigator.of(context).pushNamed('/home');

Config validation errors #

Run validation to catch issues early:

void main() {
  final errors = screenStackConfig.validate();
  assert(errors.isEmpty, 'Config errors: $errors');
}

Requirements #

  • Flutter 3.10.0 or higher
  • Dart 3.5.0 or higher
  • FlightStack CI/CD (for automated uploads)

Built by VooStack #

Need help with Flutter development or custom data grid solutions?

Contact Us

VooStack builds enterprise Flutter applications and developer tools. We're here to help with your next project.

License #

MIT License - see LICENSE for details.

1
likes
150
points
50
downloads

Publisher

verified publishervoostack.com

Weekly Downloads

Automated screenshot capture for Flutter apps. Configure routes, capture screens during CI/CD builds, and upload to ScreenStack device frames for app store assets.

Homepage
Repository (GitHub)
View/report issues

Topics

#screenshots #app-store #testing #ci-cd #automation

Documentation

Documentation
API reference

Funding

Consider supporting this project:

github.com

License

MIT (license)

Dependencies

flutter, flutter_test, integration_test

More

Packages that depend on screenstack_sdk