Flutter屏幕截图插件screenshooter的使用

Flutter屏幕截图插件screenshooter的使用

Automatically create screenshots of your flutter app.

工作原理

Screenshooter通过构建您的应用的一个特殊版本(使用不同的入口点)并在iOS模拟器上运行该版本来工作。此应用程序将执行您指定的操作,并在应截取屏幕截图时通知主机。这样可以确保屏幕截图与实际显示的内容完全一致,因为它实际上是模拟器的真实屏幕截图。

设置

要使您的应用与Screenshooter配合使用,需要完成两个步骤:

  1. 创建屏幕截图套件
  2. 创建配置文件
  3. (可选)添加框架配置

屏幕截图套件

屏幕截图套件是在客户端定义如何拍摄每个屏幕截图。创建一个应用程序的新入口点(默认为lib/screenshots.dart),并添加一个ScreenshotSuite实例并运行它。一个简单的套件可能如下所示:

// lib/screenshots.dart

import 'main.dart' as app;

void main() {
    ScreenshotSuite(
        name: 'default',
        prepare: () async {
            // 这将在其他一切之前执行。
            // 您需要在这里启动真正的应用程序
            app.main();
        },
        prepareLocale: (ScreenshotLocale locale) async {
            // 如果您稍后在配置中指定了语言环境,则会为此每个语言环境调用此函数。
            // 您可能希望在此处设置语言环境
            setAppLocale(locale); 
        },
        cleanupLocale: (ScreenshotLocale locale) async {
            // 与prepareLocale相同,但显然在拍摄完所有语言环境的屏幕截图之后运行
            clearCache();
        },
        cleanup: () {
            removeData();
        },
        screenshots: [...]
    ).run();
}

屏幕截图由一个Screenshot实例描述,这非常直观:

final screenshot = Screenshot(
    // 唯一的屏幕截图名称
    name: 'home',
    // 调用准备后的延迟(例如等待图像加载)
    delay: const Duration(seconds: 1),
    prepare: () async {
        await navigateToHome();
    },
    cleanup: () async {
        // 执行一些清理操作
    }
);

注意:所有方法都返回一个Future<void>,在继续之前会等待其完成,因此您不必担心在事物准备好之前就拍摄屏幕截图。

屏幕截图配置

如果您想在运行时访问配置变量,可以使用ScreenshotConfiguration.fromEnv。这是一个常量值,因此当用于if语句等时不会减慢您的应用程序速度。

如果要确定当前应用是否正在屏幕截图模式下运行,请使用ScreenshotConfiguration.fromEnv.isActive

如果要登录示例用户,您可以访问ScreenshotConfiguration.fromEnv.usernameScreenshotConfiguration.fromEnv.password,这些值将被传递给screenshooter。这些值来自CLI参数--username--password,或者作为(推荐)回退从环境变量SCREENSHOOTER_USERNAMESCREENSHOOTER_PASSWORD中获取。

配置文件

在设置好屏幕截图套件后,您需要指定如何运行它。尽管此时您可以直接使用命令行参数运行screenshooter,但建议使用配置文件。默认情况下将使用screenshooter.yaml文件,如果没有找到则回退到pubspec.yaml中的screenshooter键(参见使用)。

使用配置文件还可以让您高效地为多个模拟器拍摄屏幕截图。

这是一个包含所有可能选项的配置文件示例 - 它们不是必需的:

# screenshooter.yaml

bundleId: com.example.app
# 这是每个屏幕截图的路径。占位符:
# - {locale} 将被替换为当前语言环境的语言标签(BCP47)
# - {name} 替换为`Screenshot`中指定的名称
# - {device} 替换为设备标识符(如在`devices`中指定)
path: screenshots/{locale}/{name}_{device}.png
# 要创建屏幕截图的所有语言环境(以BCP47格式用破折号分隔)。如果您省略了这个,
# 则只会拍摄一次屏幕截图,`path`中的{locale}将具有值`none`
locales:
  - en-US
  - de-DE
# 这是一个所有模拟器的列表。键是iOS模拟器的确切名称,值是使用的标识符
devices:
  iPhone 14 Plus: iphone_65
  iPhone 8 Plus: iphone_55
# 用作应用程序入口点的文件名
target: lib/screenshots.dart

使用

要在项目的根目录中运行screenshooter,请使用dart run screenshooter。默认情况下,它将尝试查找screenshooter.yaml文件以读取配置。如果没有找到,则会查找pubspec.yaml中的screenshooter键。

要加载另一个配置文件(例如,如果您有多个配置),请使用dart run screenshooter -f <您的config.yaml的名称>

使用dart run screenshooter --help获取有关所有命令行参数的信息。

添加设备框架

Screenshooter还包括一个快速实现的屏幕截图框架。此步骤完全是可选的且独立于屏幕截图。使用dart run screenshooter:frame在创建屏幕截图后运行此后处理。

除了添加设备框架外,此工具还可以使用titles在框架顶部添加文本。生成的图像大小与原始屏幕截图相同。

这使用了meta设备框架,因为它们是免费提供的且相当新。对于图像处理,使用ImageMagick,因为它比纯Dart实现快得多。设备框架将自动下载到~/.cache/screenshooter-device-frames目录中。为了找到好的frameSelectors,您可以查看该目录。

注意:设备框架的下载链接不是静态的,必须从HTML页面解析。该工具应该能自动完成此操作,但如果失败了,您可以手动从这里下载存档并解压缩到~/.cache/screenshooter-device-frames目录中,以便Meta Devices子目录包含设备的文件夹。

注意事项

如何选择标题?

每个标题的关键字都会与屏幕截图的文件名进行比较。第一个包含该关键字的标题将被使用。

如何选择设备框架?

从screenshooter配置中使用的设备名称。在配置中,名称是devices映射中的键。

配置

配置从screenshooter.frames.yaml读取。如果此文件不存在,则从screenshooter.yamlpubspec.yaml中的frames键加载配置。

以下是一些可用选项,虽然并非所有都是必需的:

# 在每个屏幕截图顶部添加的文本。这些语言对应于`screenshooter.yaml`文件中`locales`部分中的语言。
# 文件名中第一个找到的该文件(例如"home","account")的键将被选中。
titles:
  en-US:
    home: 这是主页
    account: 这是账户页
  de-DE:
    shop: 这是商店页面
    account: 这是账户页面

# 在框架化屏幕截图后附加到文件名。
suffixFrame: _framed
# 在添加文本后附加到文件名。
suffixText: _text

# 选择使用哪些框架的标准。如果在目录层次结构的同一级别中有多个匹配项,则选择最短的一个。
frameSelectors:
  - shadow
  - white
  - silver
  - starlight
# 屏幕截图的背景颜色。可以是十六进制颜色或ImageMagick识别的颜色名称。
background: black
# 每边的填充百分比。相对于屏幕截图的宽度。
paddingPercent: 5

# 关于用于标题的字体设置。
font: ./fonts/OpenSans/OpenSans-Bold.ttf
fontSize: 75
fontColor: white

示例代码

以下是示例代码,演示了如何使用Screenshooter插件:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

final rootNavigatorKey = GlobalKey<NavigatorState>();
final appKey = GlobalKey<_MyAppState>();

Locale locale = const Locale('en', 'US');
List<Locale> locales = [
  const Locale('en', 'US'),
  const Locale('de', 'DE'),
];

void setLocale(Locale l) {
  locale = l;
  appKey.currentState?.restart();
}

class MyApp extends StatefulWidget {
  MyApp() : super(key: appKey);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Key key = UniqueKey();

  void restart() {
    setState(() {
      key = UniqueKey();
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Screenshooter Demo',
      key: key,
      navigatorKey: rootNavigatorKey,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: locale.countryCode == 'US' ? Colors.blue : Colors.green,
        ),
        useMaterial3: true,
      ),
      routes: {
        '/': (context) => const CustomPage(
              title: 'Home Page',
              isHome: true,
            ),
        '/other': (context) => const CustomPage(
              title: 'Other Page',
              isHome: false,
            ),
      },
    );
  }
}

class CustomPage extends StatelessWidget {
  final String title;
  final bool isHome;

  const CustomPage({
    super.key,
    required this.title,
    required this.isHome,
  });

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              'Locale: $locale',
              style: Theme.of(context).textTheme.titleLarge,
            ),
            TextButton(
              onPressed: () {
                setLocale(locales.firstWhere((element) => element != locale));
              },
              child: Text(
                'Switch to ${locales.firstWhere((element) => element != locale)}',
              ),
            ),
            if (isHome)
              ElevatedButton(
                onPressed: () {
                  rootNavigatorKey.currentState!.pushNamed('/other');
                },
                child: const Text('Go to other page →'),
              ),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter屏幕截图插件screenshooter的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter屏幕截图插件screenshooter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用screenshooter插件进行屏幕截图的示例代码。screenshooter插件允许你轻松地在Flutter应用中捕获屏幕截图。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加screenshooter插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  screenshooter: ^0.1.0  # 请检查最新版本号

然后运行flutter pub get来安装依赖。

2. 配置权限(Android)

对于Android设备,你需要在android/app/src/main/AndroidManifest.xml文件中添加存储权限,以便保存截图:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 其他配置 -->

</manifest>

3. 使用插件

在你的Flutter代码中,你可以通过Screenshooter类来捕获屏幕截图。以下是一个简单的示例:

import 'package:flutter/material.dart';
import 'package:screenshooter/screenshooter.dart';
import 'dart:io';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ScreenShooter Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'Capture Screenshot',
                style: TextStyle(fontSize: 24),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: _captureScreenshot,
                child: Text('Capture'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _captureScreenshot() async {
    try {
      File image = await Screenshooter.captureWithFileName('screenshot');
      print('Screenshot saved to ${image.path}');
      // 你可以在这里添加将截图显示到界面或者分享的逻辑
      // 例如:_showSnackbar(image.path);
    } catch (e) {
      print('Error capturing screenshot: $e');
    }
  }

  // 示例:使用Snackbar显示截图路径(可选)
  void _showSnackbar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        duration: Duration(seconds: 3),
      ),
    );
  }
}

4. 运行应用

现在,你可以运行你的Flutter应用,点击按钮时,它将会捕获当前屏幕截图并保存到设备存储中。

注意事项

  • 截图文件名和路径可能会因设备和平台而异。
  • 确保你已经处理了存储权限请求,特别是在Android 6.0(API级别23)及以上版本。
  • 插件的API和用法可能会随着版本更新而变化,请参考插件的官方文档获取最新信息。

这个示例演示了如何在Flutter应用中使用screenshooter插件进行屏幕截图。希望这对你有所帮助!

回到顶部