Flutter截屏与长截屏的实现

在做App开发中,获取当前视图的截图基本都会用到的,在Android中,我们可以通过视图的id获取当前视图的bitmap进行编辑操作,在Flutter中想获取Widget的截图针对不同的场景也是需要一个key进行绑定截图。

这里介绍的Flutter截图的方式主要分为两种:视图可见时的截图与不可见的截图。

一、可见视图截图

需要用到的组件主要有

1)GlobalKey

2)RenderRepaintBoundary

1,创建一个GlobalKey对象
GlobalKey paintKey = new GlobalKey();
2,使用RepaintBoundary包裹需要截图的Widget,并把创建的GlobalKey与之绑定
RepaintBoundary(key: paintKey,//需要注意,此处绑定创建好的GlobalKey对象child: Column(//需要截图的WidgetmainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),
)
3,根据GlobalKey对象进行截图编译或保存本地
//compressionRatio:截图的图片质量,默认值是:1.0
static Future<String?> capturePng(GlobalKey paintKey,double compressionRatio) async {try {RenderRepaintBoundary? boundary =paintKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;var image = await boundary?.toImage(pixelRatio: compressionRatio);ByteData? byteData = await image?.toByteData(format: ImageByteFormat.png);//getApplicationSupportDirectory需要引用path_provider库final directory = await getApplicationSupportDirectory();//这里需要导入import 'dart:io';很多人第一次导包会默认导入import 'dart:html';导致报错var imgFile = await File('${directory.path}/${DateTime.now().millisecondsSinceEpoch}.png').create();Uint8List? pngBytes = byteData?.buffer.asUint8List();//把Widget当前帧数据写入File文件中await imgFile.writeAsBytes(pngBytes!);return imgFile.path;} catch (e) {print(e);}return null;
}

核心就是根据之前创建的GlobalKey对象,使用RenderRepaintBoundary获取Widget渲染完成的当前帧内容保存成文件格式进行二次编辑操作,主要注意的点就是File的导包,针对不熟悉Flutter的人几乎都会遇到的一个错误,至此获取Widget截图的方式已经实现,但这只针对一个简单的截图方式。

如果要针对滑动的列表进行截图,则需要使用SingleChildScrollView包裹一层,不然无法截全,例如:

SingleChildScrollView(child: RepaintBoundary(key: paintKey,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),),
)

截取长图时,如果列表数据很长超过上千条数据时,截出来的图片就会变的很模糊,针对这种场景,建议让后端直接生成图片或者pdf,App侧直接使用或预览,毕竟App本质工作就是一个视图预览的。

二、不可见的视图截屏

开发过程中不免有些场景是不需要预览直接截图保存本地的,如果增加预览操作会影响用户的使用体验,这是就需要用到不可见的视图截屏方式。

Future<Uint8List> captureInvisibleWidget(Widget widget, {Duration delay = const Duration(seconds: 1),double? pixelRatio,BuildContext? context,Size? targetSize,}) async {ui.Image image = await widgetToUiImage(widget,delay: delay,pixelRatio: pixelRatio,context: context,targetSize: targetSize);final ByteData? byteData =await image.toByteData(format: ui.ImageByteFormat.png);image.dispose();return byteData!.buffer.asUint8List();
}/// If you are building a desktop/web application that supports multiple view. Consider passing the [context] so that flutter know which view to capture.
static Future<ui.Image> widgetToUiImage(Widget widget, {Duration delay = const Duration(seconds: 1),double? pixelRatio,BuildContext? context,Size? targetSize,}) async {//////Retry counter///int retryCounter = 3;bool isDirty = false;Widget child = widget;if (context != null) {//////Inherit Theme and MediaQuery of app//////child = InheritedTheme.captureAll(context,MediaQuery(data: MediaQuery.of(context),child: Material(child: child,color: Colors.transparent,)),);}final RenderRepaintBoundary repaintBoundary = RenderRepaintBoundary();final platformDispatcher = WidgetsBinding.instance.platformDispatcher;final fallBackView = platformDispatcher.views.first;final view = fallBackView;Size logicalSize =targetSize ?? view.physicalSize / view.devicePixelRatio; // AdaptedSize imageSize = targetSize ?? view.physicalSize; // Adaptedassert(logicalSize.aspectRatio.toStringAsPrecision(5) ==imageSize.aspectRatio.toStringAsPrecision(5)); // Adapted (toPrecision was not available)final RenderView renderView = RenderView(window: view,child: RenderPositionedBox(alignment: Alignment.center, child: repaintBoundary),configuration: ViewConfiguration(size: logicalSize,devicePixelRatio: pixelRatio ?? 1.0,),);final PipelineOwner pipelineOwner = PipelineOwner();final BuildOwner buildOwner = BuildOwner(focusManager: FocusManager(),onBuildScheduled: () {//////current render is dirty, mark it.///isDirty = true;});pipelineOwner.rootNode = renderView;renderView.prepareInitialFrame();final RenderObjectToWidgetElement<RenderBox> rootElement =RenderObjectToWidgetAdapter<RenderBox>(container: repaintBoundary,child: Directionality(textDirection: TextDirection.ltr,child: child,)).attachToRenderTree(buildOwner,);///Render Widget//////buildOwner.buildScope(rootElement,);buildOwner.finalizeTree();pipelineOwner.flushLayout();pipelineOwner.flushCompositingBits();pipelineOwner.flushPaint();ui.Image? image;do {//////Reset the dirty flag//////isDirty = false;image = await repaintBoundary.toImage(pixelRatio: pixelRatio ?? (imageSize.width / logicalSize.width));//////This delay sholud increas with Widget tree Size///await Future.delayed(delay);//////Check does this require rebuild//////if (isDirty) {//////Previous capture has been updated, re-render again.//////buildOwner.buildScope(rootElement,);buildOwner.finalizeTree();pipelineOwner.flushLayout();pipelineOwner.flushCompositingBits();pipelineOwner.flushPaint();}retryCounter--;//////retry untill capture is successfull///} while (isDirty && retryCounter >= 0);try {/// Dispose All widgets// rootElement.visitChildren((Element element) {//   rootElement.deactivateChild(element);// });buildOwner.finalizeTree();} catch (e) {}return image; // Adapted to directly return the image and not the Uint8List
}

使用时,直接调用captureInvisibleWidget 传入所需要的截图和截图质量即可获取到视图的照片数据,用于其他用途。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750828.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java请求参数校验

1.参数校验总结 注解描述Null限制只能为nullNotNull限制必须不能为nullNotEmpty验证注解的值不为null且不为空&#xff08;长度大于0&#xff09;NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0)&#xff0c;不同于NotEmpty&#xff0c;NotBlank只应用于字符…

如何在 Linux ubuntu 系统上搭建 Java web 程序的运行环境

如何在 Linux ubuntu 系统上搭建 Java web 程序的运行环境 基于包管理器进行安装 Linux 会把一些软件包放到对应的服务器上&#xff0c;通过包管理器这样的程序&#xff0c;来把这些软件包给下载安装 ubuntu系统上的包管理器是 apt centos系统上的包管理器 yum 注&#xff1a;…

C++——基础复习——模拟动态数组容器

一、功能 1、尾插 2、随机插入 3、随机访问 4、随机查找 5、删除指定下标元素 6、删除指定元素 7、字符串输出 二、功能基类 复习知识&#xff1a; 1、虚函数&#xff1a;实现动态多态&#xff0c;通过派生类的重写实现扩展功能&#xff1b;提高代码的复用性&#xf…

Maven 之 配置文件pom

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://…

代码随想录阅读笔记-哈希表【赎金信】

题目 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串&#xff0c;判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成&#xff0c;返回 true &#xff1b;否则返回 false。 (题目说明&#xff1a;为了不暴露赎金信字迹&#x…

Rabbit MQ详解

写在前面,由于Rabbit MQ涉及的内容较多&#xff0c;赶在春招我个人先按照我认为重要的内容进行一定总结&#xff0c;也算是个学习笔记吧。主要参考官方文档、其他优秀文章、大模型问答。自己边学习边总结。后面有时间我会慢慢把所有内容补全&#xff0c;分享出来也是希望可以给…

CSS学习(1)-选择器

一、基本选择器 1. 通配选择器 作用&#xff1a;可以选中所有的 HTML 元素。 语法&#xff1a; * { 属性名: 属性值; }举例&#xff1a; /* 选中所有元素 */ * { color: orange; font-size: 40px; }主要用于&#xff1a;清除样式。 2. 元素选择器 作用&#xff1a;为页面…

QT5.14.2 探索Qt字符串操作的奥秘:Qt的QString如何成为你的编程利器?

在Qt的世界里&#xff0c;字符串处理是一项基础而关键的技能。无论是从用户输入中提取数据&#xff0c;还是对数据进行格式化输出&#xff0c;掌握QString的使用都能显著提升你的开发效率。本文将带你深入探索QString&#xff0c;看看QString如何成为你的编程利器&#xff01; …

如何引入ElementUI组件库,快速上手Element

前言&#xff1a;在上篇文章中分享了如何快速上手Vue框架项目&#xff0c;本篇文章则介绍的是Element的使用&#xff0c;通过本篇文章的分享&#xff0c;我们就可以将Vue和Element结合使用&#xff0c;快速构建出精美的网页界面 目录 一.Element和ElementUI 二.如何引入Eleme…

leetcod 685. 冗余连接 II

久违的没做太出来的题目&#xff0c;leetcod 685. 冗余连接 II 题目 在本问题中&#xff0c;有根树指满足以下条件的 有向 图。该树只有一个根节点&#xff0c;所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点&#xff0c;而根节点没有…

关于Oracle Primavera P6的各数据库帐号用途

在使用/维护P6时&#xff0c;经常会用到各种不同的P6数据库用户&#xff0c;如在连接配置P6 Professional时用到的公共帐号pubuser&#xff0c;进入后台维护p6配置信息(adminpv)或开发常连接的privuser&#xff0c;亦或是配置BI Report/BUSINESS Intelligence报表套件用到的pxr…

OpenAI Q-Star:AGI距离自我意识越来越近

最近硅谷曝出一份54页的内部文件&#xff0c;揭露了去年OpenAI宫斗&#xff0c;导致Altman&#xff08;奥特曼&#xff09;差点离职的神秘项目——Q-Star&#xff08;神秘代号Q*&#xff09;。 根据该文件显示&#xff0c;Q-Star多模态大模型拥有125万亿个参数&#xff0c;比现…

ARM架构中导致独占式内存访问Exclusive access 指令(LDXR/STXR)失败的原因分析

在之前的博文当中&#xff0c;笔者介绍了ARM中支持同步和信号量的硬件实现机制&#xff1a;Exclusive access&#xff08;独占式访问&#xff09;以及Load-Exclusive/Store-Exclusive指令的使用&#xff1a; ARMv8 同步和信号量&#xff08;Synchronization and semaphores&…

什么是ping

[Q&A] ping的作用 探测本机与网络里另一主机是否已建立连接&#xff0c;常用语检测网络是否畅通&#xff0c;获取连接速度等信息。 [Q&A] ping的原理 ping命令的原理基于ICMP&#xff08;Internet Control Message Protocol&#xff09;协议&#xff0c;这是一种网络…

7.Java整合MongoDB—项目创建

整合MongoDB MongoDB的基本知识有所了解之后&#xff0c;我们开始着手上代码了&#xff0c;进来先来项目创建&#xff0c;如何引入mongodb&#xff0c;以及测试一下能否连接数据库。 1 新建springboot项目 其实只需要spring boot mongodb这个依赖就行&#xff0c;加那么多纯属…

Kubernetes kafka系列 | k8s部署kafka+zookeepe集群

一、kafka.zookeeper介绍 Kafka 简介&#xff1a; Apache Kafka 是一个开源的分布式流处理平台和消息队列系统。它最初由LinkedIn开发&#xff0c;并于2011年成为Apache软件基金会的顶级项目。 特点&#xff1a; 高吞吐量&#xff1a; Kafka 能够处理大规模的消息流&#xf…

【TB作品】MSP430,波形发生器,单片机,Proteus仿真

文章目录 题目效果梯形波100个点产生方法锯齿波100个点产生方法c代码和proteus仿真 题目 114 波形发生器的制作 设计要求 设计一个能产生正弦波、方波、三角波、梯形波、锯齿波的波形发生器。设置5个开关K1~K5(从 上到下),分别对应正弦波、方波、三角波、梯形波、锯齿波,按一下…

短剧分销怎么赚钱的?保姆级教程助你短剧cps推广赚大钱

短剧分销怎么赚钱的&#xff1f;小白也能月入过万/“蜂小推“保姆级教程&#xff0c;助你短剧分销赚大钱&#xff01; 相信大家或多或少都在某些群里看到一些“霸道总裁爱上职场小菜鸟...”“这类链接&#xff0c;无利不起早&#xff0c;为什么会有那么多在群里分享这些狗血视…

习题11-2 查找星期

本题要求实现函数&#xff0c;可以根据下表查找到星期&#xff0c;返回对应的序号。 序号星期0Sunday1Monday2Tuesday3Wednesday4Thursday5Friday6Saturday 函数接口定义&#xff1a; int getindex( char *s ); 函数getindex应返回字符串s序号。如果传入的参数s不是一个代表…

Pycharm连接远程服务器Anoconda中的虚拟环境

在配置远程解释器时&#xff0c;踩过一些坑&#xff0c;现在记录一下配置过程&#xff1a; 步骤1&#xff1a; 打开pycharm的File里面的Settings 里面的Project:你的项目名称目录下的Python Interpreter。 步骤二&#xff1a; 点击右上角的“add interpreter”&#xff0c;选择…