flutter_store_screenshots 0.1.0 copy "flutter_store_screenshots: ^0.1.0" to clipboard
flutter_store_screenshots: ^0.1.0 copied to clipboard

Harness your Flutter skills to create stunning screenshots for your app store listings. This package provides a collection of tools and utilities to help you capture high-quality screenshots of your F [...]

example/lib/main.dart

import 'package:device_frame/device_frame.dart';
import 'package:flutter/material.dart';
import 'package:flutter_store_screenshots/flutter_store_screenshots.dart';

import 'app_screens/detail_screen.dart';
import 'app_screens/home_screen.dart';
import 'app_screens/login_screen.dart';
import 'app_screens/settings_screen.dart';
import 'l10n/app_localizations.dart';
import 'theme/app_theme.dart';

void main() {
  runApp(
    FlutterStoreScreenshotsApp(
      // ── Supported locales ──────────────────────────────────────────────────
      locales: const [Locale('en'), Locale('nl'), Locale('es'), Locale('de')],
      localizationsDelegates: const [AppLocalizations.delegate],

      // ── App-level theme ────────────────────────────────────────────────────
      theme: AppTheme.light,

      // ── Screenshot sets ────────────────────────────────────────────────────
      screenshotSets: [
        // ── iOS Phone 6.7" — framedCanvas (single device, title + subtitle) ──
        //
        // framedCanvas returns a WidgetBuilder used directly as
        // StoreScreenshot.builder. Title and subtitle call AppLocalizations
        // directly on the BuildContext, which already has the right locale.
        AppleScreenshotSet.iPhone67(
          storeScreenshots: _phoneScreenshots(
            device: Devices.ios.iPhone16ProMax,
            gradient: const LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Color(0xFF1A237E), Color(0xFF7986CB)],
            ),
          ),
        ),

        // ── Android Phone ────────────────────────────────────────────────────
        AndroidScreenshotSet.phone(
          storeScreenshots: _phoneScreenshots(
            device: Devices.android.samsungGalaxyS25,
            gradient: const LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Color(0xFF1B5E20), Color(0xFF66BB6A)],
            ),
          ),
        ),

        // ── iOS iPad Pro 12.9" ───────────────────────────────────────────────
        AppleScreenshotSet.iPadPro129(
          storeScreenshots: _tabletScreenshots(
            device: Devices.ios.iPad12InchesGen4,
            gradient: const LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Color(0xFF4A148C), Color(0xFFBA68C8)],
            ),
          ),
        ),

        // ── Android Tablet 7" ────────────────────────────────────────────────
        AndroidScreenshotSet.tablet7(
          storeScreenshots: _tabletScreenshots(
            device: Devices.android.mediumTablet,
            gradient: const LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Color(0xFF006064), Color(0xFF4DD0E1)],
            ),
          ),
        ),

        // ── Android Tablet 10" ───────────────────────────────────────────────
        AndroidScreenshotSet.tablet10(
          storeScreenshots: _tabletScreenshots(
            device: Devices.android.largeTablet,
            gradient: const LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [Color(0xFF0D47A1), Color(0xFF42A5F5)],
            ),
          ),
        ),

        // ── Google Play Feature Graphic ──────────────────────────────────────
        //
        // featureGraphicCanvas returns a WidgetBuilder with marketing text on
        // the left and a device mockup on the right.
        AndroidScreenshotSet.featureGraphic(
          storeScreenshots: [
            StoreScreenshot(
              name: 'feature_graphic',
              builder: featureGraphicCanvas(
                child: (_) => const HomeScreen(),
                title: (ctx) => AppLocalizations.of(ctx).appName,
                subtitle: (ctx) =>
                    AppLocalizations.of(ctx).screenshotFeatureTitle,
              ),
            ),
          ],
        ),

        // ── Multi-device canvas ───────────────────────────────────────────────
        //
        // A single StoreScreenshot.builder can place multiple ScreenshotContent
        // widgets — each independently isolated and device-framed — anywhere in
        // the canvas. This achieves the banner-style layout where several phones
        // appear side-by-side on one image.
        //
        // Wrap each ScreenshotContent in a DeviceFrame to inject the correct
        // device-level MediaQuery for that screen.
        ScreenshotSet(
          name: 'Multi-Device Banner',
          targetPlatform: TargetPlatform.iOS,
          size: const Size(1290, 2796),
          pixelDensity: 1.0,
          storeScreenshots: [
            StoreScreenshot(
              name: 'multi_device',
              builder: (context) {
                final loc = AppLocalizations.of(context);
                return Container(
                  decoration: const BoxDecoration(
                    gradient: LinearGradient(
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                      colors: [Color(0xFF1565C0), Color(0xFF42A5F5)],
                    ),
                  ),
                  child: Column(
                    children: [
                      // ── Marketing headline ─────────────────────────────────
                      Padding(
                        padding: const EdgeInsets.fromLTRB(80, 120, 80, 0),
                        child: Text(
                          loc.screenshotHomeTitle,
                          textAlign: TextAlign.center,
                          style: const TextStyle(
                            color: Colors.white,
                            fontSize: 80,
                            fontWeight: FontWeight.bold,
                            height: 1.2,
                          ),
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.fromLTRB(80, 32, 80, 0),
                        child: Text(
                          loc.screenshotHomeSubtitle,
                          textAlign: TextAlign.center,
                          style: const TextStyle(
                            color: Colors.white70,
                            fontSize: 44,
                            height: 1.4,
                          ),
                        ),
                      ),

                      // ── Three phones side-by-side ──────────────────────────
                      //
                      // Each DeviceFrame injects the phone's viewport MediaQuery
                      // for its ScreenshotContent. showBackButton: true on the
                      // detail screen makes the ← icon visible.
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 40,
                            vertical: 60,
                          ),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            crossAxisAlignment: CrossAxisAlignment.end,
                            children: [
                              Expanded(
                                child: Padding(
                                  padding: const EdgeInsets.only(bottom: 60),
                                  child: DeviceFrame(
                                    device: Devices.ios.iPhone16ProMax,
                                    screen: ScreenshotContent(
                                      builder: (_) => const LoginScreen(),
                                    ),
                                  ),
                                ),
                              ),
                              const SizedBox(width: 24),
                              Expanded(
                                child: DeviceFrame(
                                  device: Devices.ios.iPhone16ProMax,
                                  screen: ScreenshotContent(
                                    builder: (_) => const HomeScreen(),
                                  ),
                                ),
                              ),
                              const SizedBox(width: 24),
                              Expanded(
                                child: Padding(
                                  padding: const EdgeInsets.only(bottom: 60),
                                  child: DeviceFrame(
                                    device: Devices.ios.iPhone16ProMax,
                                    screen: ScreenshotContent(
                                      showBackButton: true,
                                      builder: (_) => const DetailScreen(),
                                    ),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          ],
        ),

        // ── Fully custom canvas — no helper functions ─────────────────────────
        //
        // StoreScreenshot.builder owns the entire canvas. AppLocalizations,
        // theming, and device framing are all handled directly in the builder.
        // ScreenshotContent provides Navigator isolation without any device
        // frame — the app content fills the space its parent gives it.
        ScreenshotSet(
          name: 'Custom Canvas',
          targetPlatform: TargetPlatform.iOS,
          size: const Size(430, 932),
          pixelDensity: 3.0,
          storeScreenshots: [
            StoreScreenshot(
              name: 'custom_home',
              builder: (context) {
                final loc = AppLocalizations.of(context);
                return ColoredBox(
                  color: const Color(0xFFBF360C),
                  child: Column(
                    children: [
                      Padding(
                        padding: const EdgeInsets.fromLTRB(32, 56, 32, 0),
                        child: Text(
                          loc.screenshotHomeTitle,
                          textAlign: TextAlign.center,
                          style: const TextStyle(
                            color: Colors.white,
                            fontSize: 28,
                            fontWeight: FontWeight.w900,
                            letterSpacing: -0.5,
                          ),
                        ),
                      ),
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 16,
                            vertical: 20,
                          ),
                          child: ClipRRect(
                            borderRadius: BorderRadius.circular(32),
                            // Bare ScreenshotContent (no DeviceFrame) — fills
                            // whatever space its parent provides.
                            child: ScreenshotContent(
                              builder: (_) => const HomeScreen(),
                            ),
                          ),
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.fromLTRB(32, 0, 32, 48),
                        child: Text(
                          loc.screenshotHomeSubtitle,
                          textAlign: TextAlign.center,
                          style: const TextStyle(
                            color: Colors.white70,
                            fontSize: 16,
                            height: 1.4,
                          ),
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          ],
        ),

        // ── Per-screenshot theme override (dark mode) ─────────────────────────
        //
        // Individual StoreScreenshots can still override theme, locale, size,
        // and pixelDensity independently of the set-level defaults.
        ScreenshotSet(
          name: 'Dark Theme',
          targetPlatform: TargetPlatform.android,
          size: const Size(412, 892),
          pixelDensity: 2.625,
          storeScreenshots: [
            StoreScreenshot(
              name: 'home_dark',
              theme: ThemeData(
                useMaterial3: true,
                colorScheme: ColorScheme.fromSeed(
                  seedColor: const Color(0xFF6C63FF),
                  brightness: Brightness.dark,
                ),
              ),
              builder: framedCanvas(
                device: Devices.android.samsungGalaxyS25,
                gradient: const LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [Color(0xFF212121), Color(0xFF616161)],
                ),
                child: (_) => const HomeScreen(),
                title: (ctx) => AppLocalizations.of(ctx).screenshotHomeTitle,
                subtitle: (ctx) =>
                    AppLocalizations.of(ctx).screenshotHomeSubtitle,
              ),
            ),
            // showBackButton is controlled via ScreenshotContent — use a custom
            // builder when you need the ← icon to appear on a detail screen.
            StoreScreenshot(
              name: 'detail_dark',
              theme: ThemeData(
                useMaterial3: true,
                colorScheme: ColorScheme.fromSeed(
                  seedColor: const Color(0xFF6C63FF),
                  brightness: Brightness.dark,
                ),
              ),
              builder: (context) => Container(
                decoration: const BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                    colors: [Color(0xFF212121), Color(0xFF616161)],
                  ),
                ),
                child: Column(
                  children: [
                    Padding(
                      padding: const EdgeInsets.fromLTRB(24, 48, 24, 0),
                      child: Text(
                        AppLocalizations.of(context).screenshotDetailTitle,
                        textAlign: TextAlign.center,
                        style: const TextStyle(
                          color: Colors.white,
                          fontSize: 32,
                          fontWeight: FontWeight.bold,
                          height: 1.2,
                        ),
                      ),
                    ),
                    Expanded(
                      child: Padding(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 20,
                          vertical: 24,
                        ),
                        child: DeviceFrame(
                          device: Devices.android.samsungGalaxyS25,
                          screen: ScreenshotContent(
                            showBackButton: true,
                            builder: (_) => const DetailScreen(),
                          ),
                        ),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.fromLTRB(24, 0, 24, 48),
                      child: Text(
                        AppLocalizations.of(context).screenshotDetailSubtitle,
                        textAlign: TextAlign.center,
                        style: const TextStyle(
                          color: Colors.white70,
                          fontSize: 18,
                          height: 1.4,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),

        // ── Panoramic canvas — fan of phones ─────────────────────────────────
        //
        // Three phones arranged like a hand of cards spread across 3 screenshots.
        //
        //   screenshot 0     │  screenshot 1   │  screenshot 2
        //   ─────────────────┼─────────────────┼─────────────────
        //                 ╱──┼──╲           ╱──┼──╲
        //              ╱ left ╲ │        ╱right╲  │
        //           ╱  (Login) ╲│      ╱(Settings)╲
        //                       │  ┌─────────┐    │
        //                       │  │ center  │    │
        //                       │  │ (Home)  │    │
        //                       │  └─────────┘    │
        //
        // • Left phone   — centred on the 0→1 boundary, rotated −17°.
        //   Half visible in screenshot 0, half in screenshot 1.
        // • Right phone  — centred on the 1→2 boundary, rotated +17°.
        //   Half visible in screenshot 1, half in screenshot 2.
        // • Center phone — centred in slice 1, upright, painted last (on top).
        //   The rotation of the side phones makes their visual footprint even
        //   wider, so they bleed further into the adjacent screenshot than the
        //   layout box alone suggests.
        AppleScreenshotSet.iPhone67(
          storeScreenshots: panoramicCanvas(
            count: 3,
            names: ['panoramic_login', 'panoramic_home', 'panoramic_settings'],
            panoramaBuilder: (context) {
              // MediaQuery.sizeOf returns the full panorama size here:
              //   width  = 3 × 430 = 1290 logical px
              //   height = 932 logical px
              final size = MediaQuery.sizeOf(context);
              final sliceW = size.width / 3;
              final loc = AppLocalizations.of(context);

              // Center phone: largest, upright, prominent.
              final centerH = size.height * 0.78;
              final centerW = centerH * 0.48;

              // Side phones: slightly smaller, rotated outward to create depth.
              final sideH = size.height * 0.64;
              final sideW = sideH * 0.48;

              // Side phones sit higher on the canvas; center phone sits lower.
              // This vertical stagger reinforces the fan / depth illusion.
              final sideCy = size.height * 0.42;
              final centerCy = size.height * 0.56;

              return Stack(
                fit: StackFit.expand,
                children: [
                  // ── Seamless gradient across the full panorama ──────────────
                  Container(
                    decoration: const BoxDecoration(
                      gradient: LinearGradient(
                        begin: Alignment.topLeft,
                        end: Alignment.bottomRight,
                        colors: [
                          Color(0xFF1A237E),
                          Color(0xFF1565C0),
                          Color(0xFF42A5F5),
                        ],
                      ),
                    ),
                  ),

                  // ── Title per slice ─────────────────────────────────────────
                  for (final (i, title) in [
                    (0, loc.screenshotLoginTitle),
                    (1, loc.screenshotHomeTitle),
                    (2, loc.screenshotSettingsTitle),
                  ])
                    Positioned(
                      left: i * sliceW + 28,
                      width: sliceW - 56,
                      top: 72,
                      child: Text(
                        title,
                        textAlign: TextAlign.center,
                        style: const TextStyle(
                          color: Colors.white,
                          fontSize: 26,
                          fontWeight: FontWeight.bold,
                          height: 1.2,
                        ),
                      ),
                    ),

                  // ── Left phone: Login, straddles 0→1 boundary ──────────────
                  // Painted first → behind both other phones in z-order.
                  Positioned(
                    left: sliceW - sideW / 2,
                    top: sideCy - sideH / 2,
                    width: sideW,
                    height: sideH,
                    child: Transform.rotate(
                      angle: -0.30, // ≈ −17°
                      child: DeviceFrame(
                        device: Devices.ios.iPhone16ProMax,
                        screen: ScreenshotContent(
                          builder: (_) => const LoginScreen(),
                        ),
                      ),
                    ),
                  ),

                  // ── Right phone: Settings, straddles 1→2 boundary ──────────
                  // Painted second → behind center phone.
                  Positioned(
                    left: 2 * sliceW - sideW / 2,
                    top: sideCy - sideH / 2,
                    width: sideW,
                    height: sideH,
                    child: Transform.rotate(
                      angle: 0.30, // ≈ +17°
                      child: DeviceFrame(
                        device: Devices.ios.iPhone16ProMax,
                        screen: ScreenshotContent(
                          builder: (_) => const SettingsScreen(),
                        ),
                      ),
                    ),
                  ),

                  // ── Center phone: Home, centered in slice 1 ────────────────
                  // Painted last → on top of both side phones.
                  Positioned(
                    left: size.width / 2 - centerW / 2,
                    top: centerCy - centerH / 2,
                    width: centerW,
                    height: centerH,
                    child: DeviceFrame(
                      device: Devices.ios.iPhone16ProMax,
                      screen: ScreenshotContent(
                        builder: (_) => const HomeScreen(),
                      ),
                    ),
                  ),
                ],
              );
            },
          ),
        ),

        // ── Per-screenshot pinned locales ─────────────────────────────────────
        //
        // Each StoreScreenshot can pin its own locale. The BuildContext passed
        // to the builder already has that locale active, so AppLocalizations
        // returns the correct language automatically.
        ScreenshotSet(
          name: 'Pinned Locales',
          targetPlatform: TargetPlatform.iOS,
          size: const Size(430, 932),
          pixelDensity: 3.0,
          storeScreenshots: [
            StoreScreenshot(
              name: 'home_en',
              locale: const Locale('en'),
              builder: framedCanvas(
                device: Devices.ios.iPhone16ProMax,
                gradient: const LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [Color(0xFF880E4F), Color(0xFFEC407A)],
                ),
                child: (_) => const HomeScreen(),
                title: (ctx) => AppLocalizations.of(ctx).screenshotHomeTitle,
                subtitle: (ctx) =>
                    AppLocalizations.of(ctx).screenshotHomeSubtitle,
              ),
            ),
            StoreScreenshot(
              name: 'detail_nl',
              locale: const Locale('nl'),
              builder: framedCanvas(
                device: Devices.ios.iPhone16ProMax,
                gradient: const LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [Color(0xFF880E4F), Color(0xFFEC407A)],
                ),
                child: (_) => const DetailScreen(),
                title: (ctx) => AppLocalizations.of(ctx).screenshotDetailTitle,
                subtitle: (ctx) =>
                    AppLocalizations.of(ctx).screenshotDetailSubtitle,
              ),
            ),
            StoreScreenshot(
              name: 'login_de',
              locale: const Locale('de'),
              captureDelay: const Duration(seconds: 1),
              builder: framedCanvas(
                device: Devices.ios.iPhone16ProMax,
                gradient: const LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [Color(0xFF880E4F), Color(0xFFEC407A)],
                ),
                child: (_) => const LoginScreen(),
                title: (ctx) => AppLocalizations.of(ctx).screenshotLoginTitle,
                subtitle: (ctx) =>
                    AppLocalizations.of(ctx).screenshotLoginSubtitle,
              ),
            ),
          ],
        ),
      ],
    ),
  );
}

// ── Helpers ───────────────────────────────────────────────────────────────────
//
// Define your own helper functions to avoid repeating framedCanvas arguments
// across screenshots in a set. This is the idiomatic Dart replacement for the
// old set-level `decorator` parameter.

List<StoreScreenshot> _phoneScreenshots({
  required DeviceInfo device,
  required Gradient gradient,
}) => [
  StoreScreenshot(
    name: 'login',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const LoginScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotLoginTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotLoginSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'home',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const HomeScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotHomeTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotHomeSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'detail',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const DetailScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotDetailTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotDetailSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'settings',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const SettingsScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotSettingsTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotSettingsSubtitle,
    ),
  ),
];

List<StoreScreenshot> _tabletScreenshots({
  required DeviceInfo device,
  required Gradient gradient,
}) => [
  StoreScreenshot(
    name: 'login',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const LoginScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotLoginTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotLoginSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'home',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const HomeScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotHomeTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotHomeSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'detail',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const DetailScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotDetailTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotDetailSubtitle,
    ),
  ),
  StoreScreenshot(
    name: 'settings',
    builder: framedCanvas(
      device: device,
      gradient: gradient,
      child: (_) => const SettingsScreen(),
      title: (ctx) => AppLocalizations.of(ctx).screenshotSettingsTitle,
      subtitle: (ctx) => AppLocalizations.of(ctx).screenshotSettingsSubtitle,
    ),
  ),
];
1
likes
150
points
52
downloads

Documentation

API reference

Publisher

verified publisherveenstra.dev

Weekly Downloads

Harness your Flutter skills to create stunning screenshots for your app store listings. This package provides a collection of tools and utilities to help you capture high-quality screenshots of your Flutter applications, making it easier than ever to showcase your app's features and design in the app stores.

Repository (GitHub)
View/report issues

Topics

#screenshots #store #app-store #google-play #tools

License

BSD-3-Clause (license)

Dependencies

device_frame, flutter, flutter_localizations

More

Packages that depend on flutter_store_screenshots