【Flutter 问题系列第 8 篇】在 Flutter 中如何截取屏幕并显示到页面中,以及如何将截图保存到相册

关于在 Flutter 中如何截取屏幕,以及如何将截图保存到相册的文章少之又少,即使有,也是错误一大片,有的甚至运行后都报错,就这都直接发出来了,真是可恶啊!

所以我整理了这篇博客,实现了两个功能

  • 如何截取整个屏幕或屏幕中的某一部分,并显示到页面中
  • 如何将截取的图片,保存到相册中

这两个功能都已亲测并无问题,源码会全部奉上,如果有用,希望可以给个三连,接下来是博客正文。

一:如何截取屏幕,并显示到页面中

依照国际惯例,先上效果图(没有先看到效果图,估计你们都会走吧~)

由效果图可以看出来,截取到的每一帧都是不同的图片,功能实现了,接下来就是如何实现功能的代码了。

1:RepaintBoundary 组件介绍

我们知道在 Flutter 中万物皆组件,所以接下来要说的截图其实也是一个组件,与其说是截取屏幕,不如说是截取组件。

而这个组件的名称就是 RepaintBoundary ,源码如下所示

 RepaintBoundary({ Key key, Widget child }) 

使用起来也很简单,直接套在你想要截的组件上面就行了,如果你要截取的是整个页面,套在 Scaffold 外面即可。

因为 RepaintBoundary 继承自 SingleChildRenderObjectWidget 而我们又需要获取到被截取组件的状态,所以第一个参数 Key 的类型应为 GlobalKey,如下所示

GlobalKey _repaintKey = GlobalKey(); // 可以获取到被截图组件状态的 GlobalKey 

而第二个参数就是你需要截取的组件,如下代码所示

RepaintBoundary(
  key: _repaintKey,
  child: Image.asset("assets/girl.gif", width: 200, height: 200, fit: BoxFit.cover),
) 

2:如何截图

接下来要说的是最核心的部分了,就是如何获取到截取图片的数据。

这里我直接把代码复制到下方了,关键代码都有解释,相信大家一看就懂了。

 /// 获取截取图片的数据
  Future<Uint8List> _getImageData() async {
    BuildContext buildContext = _repaintKey.currentContext;
    if (buildContext != null) {
      RenderRepaintBoundary boundary = buildContext.findRenderObject();
      // 第一次执行时,boundary.debugNeedsPaint 为 true,此时无法截图(如果为true时直接截图会报错)
      if (boundary.debugNeedsPaint) {
        // 延时一定时间后,boundary.debugNeedsPaint 会变为 false,然后可以正常执行截图的功能
        await Future.delayed(Duration(milliseconds: 20));
        // 重新调用方法
        return _getImageData();
      }
      // 获取当前设备的像素比
      double dpr = ui.window.devicePixelRatio;
      // pixelRatio 代表截屏之后的模糊程度,因为不同设备的像素比不同
      // 定义一个固定数值显然不是最佳方案,所以以当前设备的像素为目标值
      ui.Image image = await boundary.toImage(pixelRatio: dpr);
      ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
      Uint8List imageBytes = byteData.buffer.asUint8List();
      // 返回图片的数据
      return imageBytes;
    }
  } 

这里将图片数据以 Uint8List 的格式返回,方便后面显示图片。

3:如何在页面中显示截图

我们可以通过 Image.memory(); 方法从内存中加载图片,而该方法需要传入图片的数据,数据类型是 Uint8List ,这也是为什么要把图片数据以 Uint8List 类型返回了。

如果要显示的截图有多张,则定义一个列表

List<Uint8List> _images = []; // 存放所有截图的列表 

然后当点击底部按钮时,执行如下函数

/// 执行截图并显示到页面中
void _doScreenShots() async {
  Uint8List data = await _getImageData();
  _images.add(data);
  setState(() {});
} 

最后就是遍历这个列表,把图片显示出来就行了,如下代码所示

GridView.builder(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
  ),
  itemCount: _images.length,
  itemBuilder: (BuildContext context, int index) {
    if (_images.isEmpty) {
      return Container();
    }
    return Image.memory(_images[index], fit: BoxFit.cover);
  },
) 

至此,如何截取屏幕,并显示到页面中便介绍完毕了,一定要注意的是,当 _getImageData() 方法中的 boundary.debugNeedsPaint 为 true 时,一定不要去截图,一定不要去截图,一定不要去截图,否则会报

‘!debugNeedsPaint’:is not true 的错误,切记!!!

二:如何将截取到的图片,保存到相册中

如何截图已经说过了,如何将截图保存到相册中呢?

1:配置权限、引用插件

保存到相册的话就要涉及到存储文件的权限,以及如何把图片保存到相册的问题了。

这里引用两个插件

然后在 pubspec.yaml 文件中引入这两个插件,如下所示

dependencies:
  permission_handler: ^8.1.1 # 权限控制插件 by Allen Su
  image_gallery_saver: ^1.6.9 # 图片存储到相册插件 by Allen Su 

安卓系统,需要在 android/app/src/main/AndroidManifest.xml 文件中添加如下代码

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!--写入外部存储权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!--读取外部存储权限--> 

苹果系统,需要在 ios/Runner/Info.plist 文件中添加如下代码

<key>NSPhotoLibraryAddUsageDescription</key>
<string>请允许APP保存图片到相册</string> 

(因博主是从事安卓开发的,关于 ios 这里是翻阅的资料,并没有证实,应该没有什么问题)

2:存储图片到相册

权限问题解决了,接下来就是如何把截图存储到本地相册了,很简单,其实一行代码就可以了,下面的代码包含获取存储权限和存储图片到相册,如下所示

/// 执行存储图片到本地相册
void _doSaveImage() async {
  // 如果用户已授权存储权限
  if (await Permission.storage.request().isGranted) {
    Uint8List data = await _getImageData();
    await ImageGallerySaver.saveImage(data);
  } else {
    // 没有存储权限时,弹出没有存储权限的弹窗
  }
} 

当点击按钮时,是获取权限,如下图所示

点击允许后,图片会自动保存到相册,如下图所示

可以看到,这是已经保存到相册后的视图了,至此,关于在 Flutter 中如何截取屏幕并显示到页面中,以及如何将截图保存到相册便介绍完毕了,按照我写的一步一步来,应该不会有什么问题。

你的问题得到解决了吗?欢迎在评论区留言。

赠人玫瑰,手有余香,如果觉得文章不错,希望可以给个一键三连,感谢。


文末

要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】↓↓↓
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Android_XG/article/details/125335221#comments_25466710