Flutter使用screenshot进行截屏和截长图以及分享保存的全流程指南

文章目录

  • [1. 前言](#1. 前言)
  • [2. 插件核心 API](#2. 插件核心 API)
  • [3. 基础使用](#3. 基础使用)
    • [3.1. 引入依赖](#3.1. 引入依赖)
    • [3.2. 创建控制器与包裹 Widget](#3.2. 创建控制器与包裹 Widget)
    • [3.3. 执行截图](#3.3. 执行截图)
  • [4. 高级功能](#4. 高级功能)
    • [4.1. 捕获不可见的 Widget](#4.1. 捕获不可见的 Widget)
    • [4.2. 捕获超长的列表 Widget](#4.2. 捕获超长的列表 Widget)
    • [4.3. 保存到指定路径](#4.3. 保存到指定路径)
  • [4. 截图的像素与大小控制](#4. 截图的像素与大小控制)
    • [4.1. 像素调整(控制清晰度)](#4.1. 像素调整(控制清晰度))
    • [4.2. 大小控制(控制图片尺寸)](#4.2. 大小控制(控制图片尺寸))
  • [5. 实现原理](#5. 实现原理)
  • [6. 保存到系统相册](#6. 保存到系统相册)
    • [6.1. 引入依赖](#6.1. 引入依赖)
    • [6.2. 权限配置](#6.2. 权限配置)
    • [6.3. 保存代码实现](#6.3. 保存代码实现)
  • [7. 分享图片](#7. 分享图片)
    • [7.1. 引入依赖](#7.1. 引入依赖)
    • [7.2. 分享代码实现](#7.2. 分享代码实现)
  • [8. 注意事项](#8. 注意事项)
  • [9. 总结](#9. 总结)

1. 前言

最近在项目里做了截屏和长图分享功能,所以结合自己的使用经历,写一个教程和总结。本文基于 screenshot: ^3.0.0 版本编写,适配 Flutter 3.0+,支持 iOS、Android、Windows和 Web 平台。可捕获屏幕上可见/不可见的 Widget,满足截图、长图、保存、分享等场景需求。

官方文档:传送门

2. 插件核心 API

Screenshot组件,包裹需要截图的 Widget,为截图提供渲染边界,参数如下:

属性 类型 参数 说明
controller 控制器 ScreenshotController 截图调度核心实例,负责触发截图、处理截图结果
child Widget - 需要截图的 Widget

ScreenshotController控制器,方法如下:

方法 类型 参数 说明
capture() 方法 pixelRatio: double(可选)delay: Duration(可选) 捕获已渲染 Widget,返回 Uint8List 类型图片数据
captureFromWidget() 方法 Widget(必传)、pixelRatio(可选)、delay(可选) 捕获未渲染(不可见)Widget,无需包裹在 Screenshot 内
captureFromLongWidget() 方法 Widget、context、delay、constraints(可选) 捕获超长不可见列表 Widget,需传入上下文和约束
captureAndSave() 方法 path(必传)、fileName(可选)、pixelRatio(可选) 捕获并保存到指定路径(Web 不支持该方法)

3. 基础使用

下面是分别是依赖项引入和最基础的截图示例:

3.1. 引入依赖

yaml 复制代码
dependencies:
  screenshot: ^3.0.0

3.2. 创建控制器与包裹 Widget

dart 复制代码
final ScreenshotController _myScreenshotController = ScreenshotController();

Screenshot(
  controller: _myScreenshotController,
  child: const Text("Flutter Screenshot Demo"),
)

3.3. 执行截图

dart 复制代码
Uint8List? _image;

_myScreenshotController.capture(
  pixelRatio: 1.5,
  delay: const Duration(milliseconds: 10),
).then((value) {
  setState(() => _image = value);
}).catchError((e) {
  debugPrint(e.toString());
});

4. 高级功能

下面是一些高级功能的简易例子:

4.1. 捕获不可见的 Widget

dart 复制代码
_myScreenshotController.captureFromWidget(
  Container(
    padding: const EdgeInsets.all(20),
    color: Colors.blue,
    child: const Text("Invisible Widget"),
  ),
).then((value) {
  // ......处理图片(如保存、展示)
});

4.2. 捕获超长的列表 Widget

dart 复制代码
final _longWidget = Column(
  mainAxisSize: MainAxisSize.min,
  children: List.generate(50, (i) => Text("Item $i")),
);

_myScreenshotController.captureFromLongWidget(
  InheritedTheme.captureAll(context, Material(child: _longWidget)),
  context: context,
  delay: const Duration(milliseconds: 100),
).then((value) {
  // 处理长图
});

4.3. 保存到指定路径

dart 复制代码
import 'package:path_provider/path_provider.dart';

final dir = await getApplicationDocumentsDirectory();
await _myScreenshotController.captureAndSave(
  dir.path,
  fileName: "screenshot_${DateTime.now().millisecondsSinceEpoch}.png",
);

4. 截图的像素与大小控制

截图的像素清晰度和尺寸大小,主要通过以下两种方式控制,适配不同场景需求:

4.1. 像素调整(控制清晰度)

通过 capture()captureFromWidget() 等方法的 pixelRatio 参数控制,该参数表示"像素密度比",默认值为 1.0。

  • pixelRatio = 1.0:默认清晰度,适配设备基础像素,文件体积较小;
  • pixelRatio = 1.5~2.0:常用清晰值,兼顾清晰度和文件体积,适合大多数场景;
  • pixelRatio ≥ 3.0:高清模式,适合需要放大查看的场景(如海报、凭证),但文件体积会显著增大。
dart 复制代码
// 高清截图示例:pixelRatio设为3.0
Uint8List? _highDefImage = await _myScreenshotController.capture(pixelRatio: 3.0);

4.2. 大小控制(控制图片尺寸)

分两种场景控制,核心是约束目标 Widget 的尺寸,截图会自动适配 Widget 实际渲染尺寸:

  • 可见 Widget:通过父容器的 widthheight 约束 Screenshot 组件的尺寸,截图尺寸与约束尺寸一致;
  • 不可见 Widget:在 captureFromWidget() 中,给目标 Widget 包裹 Container 并设置固定宽高,或通过 constraints 参数指定尺寸。
dart 复制代码
// 控制不可见Widget截图尺寸(固定宽高300x200)
_myScreenshotController.captureFromWidget(
  Container(
    width: 300,
    height: 200,
    child: const Text("Fixed Size Widget"),
  ),
);

5. 实现原理

screenshot 插件的核心实现依赖 Flutter 渲染机制中的 RenderRepaintBoundary 组件。该组件会为其包裹的 Widget 创建独立的渲染图层,避免与其他 Widget 重复渲染,同时允许单独捕获该图层的像素数据。

其流程为:通过 Screenshot 组件给目标 Widget 绑定 RenderRepaintBoundary;ScreenshotController 调用截图方法时,会获取该渲染边界的 RenderObject;通过 RenderObject 的 toImage() 方法将渲染结果转为 Image 对象;再将 Image 编码为 Uint8List(图片字节流),最终返回给开发者用于后续处理(保存、分享等)。

对于不可见/长图捕获,插件通过手动创建渲染上下文,强制 Widget 完成渲染后再执行截图操作,突破了"仅能捕获可见 Widget"的限制。

6. 保存到系统相册

该功能使用 gal 插件实现相册保存。gal 插件适配性强,支持多平台,当前使用版本 gal: ^2.3.2

PS:也可以使用image_gallery_saver插件,有啥用啥。

6.1. 引入依赖

yaml 复制代码
dependencies:
  screenshot: ^3.0.0
  gal: ^2.3.2
  # 用于申请存储权限
  permission_handler: ^11.3.1

6.2. 权限配置

Android:在 AndroidManifest.xml 中添加存储权限

xml 复制代码
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30"/>

iOS:在 Info.plist 中添加相册权限

plist 复制代码
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要访问相册以保存截图</string>

6.3. 保存代码实现

dart 复制代码
import 'package:gal/gal.dart';
import 'package:permission_handler/permission_handler.dart';

// 截图并保存到相册
void saveScreenshotToGallery() async {
  // 申请存储/相册权限
  var status = await Permission.storage.request();
  if (!status.isGranted) {
    debugPrint("权限申请失败,无法保存相册");
    return;
  }

  // 执行截图
  Uint8List? image = await _myScreenshotController.capture(pixelRatio: 1.5);
  if (image == null) {
    debugPrint("截图失败");
    return;
  }

  // 调用gal插件保存到相册
  await Gal.putImageBytes(image, album: "Flutter截图");
  debugPrint("保存相册成功");
}

7. 分享图片

使用 share_plus: ^12.0.1插件实现截图分享,支持多平台(iOS、Android、Web 等),可分享图片给其他应用。

7.1. 引入依赖

yaml 复制代码
dependencies:
  screenshot: ^3.0.0
  # 分享
  share_plus: ^12.0.1
  # 查找文件系统中常用位置 用于临时存储图片
  path_provider: ^2.1.4

7.2. 分享代码实现

dart 复制代码
import 'package:share_plus/share_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';

// 截图并分享
void shareScreenshot() async {
  // 执行截图
  Uint8List? image = await _myScreenshotController.capture(pixelRatio: 1.5);
  if (image == null) {
    debugPrint("截图失败,无法分享");
    return;
  }

  // 保存图片到临时目录(分享需要本地文件路径)
  final tempDir = await getTemporaryDirectory();
  final tempFile = File("${tempDir.path}/screenshot_share.png");
  await tempFile.writeAsBytes(image);

  // 调用分享功能
  await Share.shareXFiles(
    [XFile(tempFile.path)],
    text: "分享一张Flutter截图",
  );
}

8. 注意事项

  • 不支持 Platform View(如地图、相机、WebView),捕获此类组件会出现黑屏或空白。
  • 图片模糊可提高 pixelRatio(建议 1.5--3.0),但会增加文件体积和截图耗时。
  • 若出现截图黑屏/空白,大概率是 Widget 未渲染完成,添加delay: Duration(milliseconds: 10--100) 即可解决。
  • captureAndSave() 不支持 Web 平台,Web 端需通过 capture() 获取字节流后,通过浏览器 API 下载。
  • 使用 gal 插件保存相册时,Android 11+ 需申请 MANAGE_EXTERNAL_STORAGE 权限,iOS 需配置相册权限描述。

9. 总结

screenshot 是 Flutter 稳定的 Widget 截图方案,基于 RenderRepaintBoundary 实现,支持可见/不可见/长图捕获。配合 gal 插件可实现相册保存,结合 share_plus 可完成分享,通过 pixelRatio 和尺寸约束可控制截图效果。使用时注意权限、渲染延迟与平台限制,可满足多数商业应用截图需求。

本文仅是简单案例,实际项目中有各种奇怪的需求,可以在评论区讨论,大家一起集思广益,分享解决方案。


本次分享就到这儿啦,我是鹏多多,深耕前端的技术创作者,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

PS:在本页按F12,在console中输入document.querySelectorAll('.tool-item-href')[0].click(),有惊喜哦~

往期文章

个人主页

相关推荐
LawrenceLan2 小时前
37.Flutter 零基础入门(三十七):SnackBar 与提示信息 —— 页面反馈与用户交互必学
开发语言·前端·flutter·dart
少云清2 小时前
【UI自动化测试】1_IOS自动化测试 _环境搭建
ui·ios
迪巴拉15252 小时前
基于Vue与Spring Boot+Open Cv的智慧校园考勤系统
前端·vue.js·spring boot
swipe2 小时前
JavaScript 对象与属性描述符:从原理到实战
前端·javascript·面试
&活在当下&2 小时前
Vue3 h函数用法详解
前端·javascript·vue.js
小贵子的博客2 小时前
(vue3错误处理)has naming conflicts with other components, ignored.
前端·javascript·vue.js
木斯佳2 小时前
前端八股文面经大全:字节跳动音视频前端一面·下(2026-03-03)·面经深度解析
前端·音视频·状态模式
西西学代码2 小时前
Flutter---路由与导航
服务器·前端·javascript