当前位置: 首页 > news >正文

Flutter性能揭秘之RepaintBoundary

作者:xuyisheng

Flutter会在屏幕上绘制Widget。如果一个Widget的内容需要更新,那就只能重绘了。尽管如此,Flutter同样会重新绘制一些Widget,而这些Widget的内容仍有部分未被改变。这可能会影响应用程序的执行性能,有时影响会非常巨大。如果您正在寻找一种方法,来防止不必要的部分重绘,您可以考虑利用RepaintBoundary。

在这篇博客理,我们将探讨Flutter中的RepaintBoundary。我们将看到如何实现RepaintBoundary的演示程序以及如何在您的flutter应用程序中使用它。

RepaintBoundary

RepaintBoundary类是Null安全的。首先,你需要了解什么是Flutter中的RepaintBoundary。它是一个为它的Child设置不同的展示层级的Widget。这个Widget为它的Child设置了一个不同的展示层级,如果一个子树与它周围的部分相比,会在意想不到的短时间内重新绘制,Flutter建议你使用RepaintBoundary来进一步提高性能。

为什么需要使用RepaintBoundary呢。

Flutter Widget与RenderObjects有关。一个RenderObject有一个叫做paint的函数,它被用来执行绘画过程。尽管如此,无论相关组件的内容是否发生变化,都可以使用绘制方法。这是因为,如果其中一个RenderObjects被设定为dirty,Flutter可能会对类似Layer中的其他RenderObjects进行重新绘制。当一个RenderObject需要利用RenderObject.markNeedsPaint进行重绘的时候,它就会建议它最接近的前辈进行重绘。祖先也会对它的前辈做同样的事情,直到根RenderObject。当一个RenderObject的paint策略被启动时,它在类似层中的所有相关RenderObjects都将被重新paint。

而有时,当一个RenderObject应该被重绘时,类似层中的其他RenderObjects不应该被重绘,因为它们的绘制产物保持不变。因此,如果我们只是对某些RenderObjects进行重绘,那会更好。利用RepaintBoundary可以帮助我们在渲染树上限制markNeedsPaint的生成,在渲染树下限制paintChild的生成。

RepaintBoundary可以将先前的渲染对象与相关的渲染对象解耦。通过这种方式,只对内容发生变化的子树进行重绘是可行的。利用RepaintBoundary可以进一步提高应用程序的执行效率,特别是当不应该被重绘的子树需要大量的工作来重绘时。

我们将做一个简单的演示程序,背景是利用CustomPainter绘制的,有10000个椭圆。同时还有一个光标,在客户接触到屏幕的最后一个位置后移动。下面是没有RepaintBoundary的代码。

示例

在正文中,我们将创建一个Stack widget。在里面,我们将添加一个StackFit.expand,并添加两个部件:_buildBackground(),和_buildCursor()。我们将定义以下代码。

Stack(fit: StackFit.expand,children: <Widget>[_buildBackground(),_buildCursor(),],
),

_buildBackground() widget

在_buildBackground()小组件中。我们将返回CustomPaint() widget。在里面,我们将在绘画器上添加BackgroundColor类。我们将在下面定义。另外,我们将添加isComplex参数为true,这意味着是否提示这个图层的绘画应该被缓存,willChange是false意味着是否应该告诉光栅缓存,这个绘画在下一帧可能会改变。

Widget _buildBackground() {return CustomPaint(painter:  BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,);
}

BackgroundColor class

我们将创建一个BackgroundColor来扩展CustomPainter。

import 'dart:math';
import 'package:flutter/material.dart';class BackgroundColor extends CustomPainter {static const List<Color> colors = [Colors.orange,Colors.purple,Colors.blue,Colors.green,Colors.purple,Colors.red,];Size _size;BackgroundColor(this._size);@overridevoid paint(Canvas canvas, Size size) {final Random rand = Random(12345);for (int i = 0; i < 10000; i++) {canvas.drawOval(Rect.fromCenter(center: Offset(rand.nextDouble() * _size.width - 100,rand.nextDouble() * _size.height,),width: rand.nextDouble() * rand.nextInt(150) + 200,height: rand.nextDouble() * rand.nextInt(150) + 200,),Paint()..color = colors[rand.nextInt(colors.length)].withOpacity(0.3));}}@overridebool shouldRepaint(BackgroundColor other) => false;
}

_buildCursor() widget

在这个Widget,我们将返回Listener Widget。我们将在onPointerDown/Move方法中添加_updateOffset()组件,并添加CustomPaint。在里面,我们将添加一个Key和CursorPointer类。我们将在下面定义。另外,我们将添加ConstrainedBox()。

Widget _buildCursor() {return Listener(onPointerDown: _updateOffset,onPointerMove: _updateOffset,child: CustomPaint(key: _paintKey,painter: CursorPointer(_offset),child: ConstrainedBox(constraints: BoxConstraints.expand(),),),);
}

CursorPointer class

我们将创建一个CursorPointer来扩展CustomPainter。

import 'package:flutter/material.dart';class CursorPointer extends CustomPainter {final Offset _offset;CursorPointer(this._offset);@overridevoid paint(Canvas canvas, Size size) {canvas.drawCircle(_offset,10.0,new Paint()..color = Colors.green,);}@overridebool shouldRepaint(CursorPointer old) => old._offset != _offset;
}

当我们运行应用程序时,我们应该得到下面屏幕的输出,如屏幕下的视频。如果你试图在屏幕上移动指针,应用程序将非常滞后,因为它重新绘制背景,需要昂贵的计算。

下面,我们将添加RepaintBoundary。解决上述问题的答案是将CustomPaint部件包装成RepaintBoundary的子Widget。

Widget _buildBackground() {return RepaintBoundary(child: CustomPaint(painter:  BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,),);
}

当我们运行应用程序时,我们应该得到屏幕的输出,就像屏幕下面的视频一样。有了这个简单的改变,现在当Flutter重绘光标时,背景就不需要重绘了。应用程序应该不再是滞后的了。

整个代码如下所示。

import 'package:flutter/material.dart';
import 'package:flutter_repaint_boundary_demo/background_color.dart';
import 'package:flutter_repaint_boundary_demo/cursor_pointer.dart';class HomePage extends StatefulWidget {@overrideState createState() => new _HomePageState();
}class _HomePageState extends State<HomePage> {final GlobalKey _paintKey = new GlobalKey();Offset _offset = Offset.zero;Widget _buildBackground() {return RepaintBoundary(child: CustomPaint(painter:  BackgroundColor(MediaQuery.of(context).size),isComplex: true,willChange: false,),);}Widget _buildCursor() {return Listener(onPointerDown: _updateOffset,onPointerMove: _updateOffset,child: CustomPaint(key: _paintKey,painter: CursorPointer(_offset),child: ConstrainedBox(constraints: BoxConstraints.expand(),),),);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(automaticallyImplyLeading: false,backgroundColor: Colors.cyan,title: const Text('Flutter RepaintBoundary Demo'),),body: Stack(fit: StackFit.expand,children: <Widget>[_buildBackground(),_buildCursor(),],),);}_updateOffset(PointerEvent event) {RenderBox? referenceBox = _paintKey.currentContext?.findRenderObject() as RenderBox;Offset offset = referenceBox.globalToLocal(event.position);setState(() {_offset = offset;});}
}

总结

在文章中,我解释了Flutter中RepaintBoundary的基本结构;你可以根据你的选择来修改这个代码。这是我对RepaintBoundary On User Interaction的一个小的介绍,它在使用Flutter时是可行的。

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

相关文章:

Flutter性能揭秘之RepaintBoundary

作者&#xff1a;xuyisheng Flutter会在屏幕上绘制Widget。如果一个Widget的内容需要更新&#xff0c;那就只能重绘了。尽管如此&#xff0c;Flutter同样会重新绘制一些Widget&#xff0c;而这些Widget的内容仍有部分未被改变。这可能会影响应用程序的执行性能&#xff0c;有时…...

29.Netty源码之服务端启动:创建EventLoopSelector流程

highlight: arduino-light 源码篇&#xff1a;从 Linux 出发深入剖析服务端启动流程 通过前几章课程的学习&#xff0c;我们已经对 Netty 的技术思想和基本原理有了初步的认识&#xff0c;从今天这节课开始我们将正式进入 Netty 核心源码学习的课程。希望能够通过源码解析的方式…...

Kotllin实现ArrayList的基本功能

前言 上次面试时&#xff0c;手写ArrayList竟然翻车&#xff0c;忘了里面的扩容与缩容的条件&#xff0c;再次实现一次&#xff0c;加深印象 源码讲了什么 实现了List列表和RandomAccess随机访问接口List具有增删改查功能&#xff0c;RandomAccess支持下标访问内部是一个扩容…...

C++的初步介绍,以及C++与C的区别

C和C的区别 C又称C plus plus&#xff0c;且C语言是对C语言的扩充&#xff0c;几乎支持所有的C语言语法&#xff1b;C语言&#xff1a;面向过程的语言&#xff08;注重问题的解决方法和算法&#xff09;C&#xff1a;面向对象的语言 &#xff08;求解的方法&#xff09;面向对…...

JDK 核心jar之 rt.jar

一、JDK目录展示 二、rt.jar 简介 2.1.JAR释义 在软件领域&#xff0c;JAR文件&#xff08;Java归档&#xff0c;英语&#xff1a;Java Archive&#xff09;是一种软件包文件格式&#xff0c;通常用于聚合大量的Java类文件、相关的元数据和资源&#xff08;文本、图片等&…...

el-form表单验证:只在点击保存时校验(包含select、checkbox、radio)

1、input类型 input类型 在el-input里加入:validate-event"false" <el-form-item label"活动名称" prop"name"><el-input v-model"ruleForm.name" :validate-event"false"></el-input> </el-form-i…...

Golang基本语法(上)

1. 变量与常量 Golang 中的标识符与关键字 标识符 Go语言中标识符由字母数字和_(下划线&#xff09;组成&#xff0c;并且只能以字母和_开头。 举几个例子&#xff1a;abc, _, _123, a123。 关键字 关键字和保留字都不建议用作变量名&#xff1a; Go语言中有25个关键字。 此…...

jenkins使用

安装插件 maven publish over ssh publish over ssh 会将打包后的jar包&#xff0c;通过ssh推送到指定的服务器上&#xff0c;&#xff0c;在jenkins中设置&#xff0c;推送后脚本&#xff0c;实现自动部署jar包&#xff0c;&#xff0c; 装了这个插件之后&#xff0c;可以在项…...

多线程基础篇(包教包会)

文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)&#xff08;重点&#…...

Android/Java中,各种数据类型之间的互相转换,给出各种实例,附上中文注释

目录 1.字符串&#xff08;String&#xff09;转整数&#xff08;int&#xff09;&#xff1a; 2.整数&#xff08;int&#xff09;转字符串&#xff08;String&#xff09;&#xff1a; 3.字符串&#xff08;String&#xff09;转浮点数&#xff08;float&#xff09;&…...

机器学习知识点总结:什么是EM(最大期望值算法)

什么是EM(最大期望值算法) 在现实生活中&#xff0c;苹果百分百是苹果&#xff0c;梨百分白是梨。 生活中还有很多事物是概率分布&#xff0c;比如有多少人结了婚&#xff0c;又有多少人有工作&#xff0c; 如果我们想要调查人群中吸大麻者的比例呢&#xff1f;敏感问题很难得…...

漏洞挖掘和安全审计的技巧与策略

文章目录 漏洞挖掘&#xff1a;发现隐藏的弱点1. 源代码审计&#xff1a;2. 黑盒测试&#xff1a;3. 静态分析工具&#xff1a; 安全审计&#xff1a;系统的全面评估1. 渗透测试&#xff1a;2. 代码审计&#xff1a;3. 安全策略审查&#xff1a; 代码示例&#xff1a;SQL注入漏…...

[SpringBoot3]Web服务

五、Web服务 基于浏览器的B/S结构应用十分流行。SpringBoot非常适合Web应用开发&#xff0c;可以使用嵌入式Tomcat、Jetty、Undertow或Netty创建一个自包含的HTTP服务器。一个SpringBoot的Web应用能够自己独立运行&#xff0c;不依赖需要安装的Tomcat、Jetty等。SpringBoot可以…...

构建系统自动化-autoreconf

autoreconf简介 autoreconf是一个GNU Autotools工具集中的一个命令&#xff0c;用于自动重新生成构建系统的配置脚本和相关文件。 Autotools是一组用于自动化构建系统的工具&#xff0c;包括Autoconf、Automake和Libtool。它们通常用于跨平台的软件项目&#xff0c;以便在不同…...

Mysql之InnoDB和MyISAM的区别

InnoDB和MyISAM是MySQL数据库中两种常见的存储引擎&#xff0c;它们在功能和性能方面有一些明显的区别。下面是它们之间的详细解释和说明&#xff1a; 底层数据 存数据的时候&#xff0c;MyISAM是数据和索引分开存储&#xff0c;分为MYD和MYI 而InnoDB是数据即索引&#xff0…...

Unity 之 Transform.Translate 实现局部坐标系中进行平移操作的方法

文章目录 Translate 默认使用局部坐标也可以转换成世界坐标 Translate 默认使用局部坐标 在Unity中&#xff0c;Transform.Translate是用于在游戏对象的局部坐标系中进行平移操作的方法。这意味着它将游戏对象沿着其自身的轴进行移动&#xff0c;而不是世界坐标轴。这在实现物…...

PostgreSQL Error: sorry, too many clients already

Error PG的默认最大连接数是100. 如果超过100就会报错sorry, too many clients already Find show max_connections; SELECT COUNT(*) from pg_stat_activity; SELECT * FROM pg_stat_activity;Solution 提高最大连接数 ALTER SYSTEM SET max_connections 然后重启pg查看…...

Vue2(路由)

目录 一&#xff0c;路由原理&#xff08;hash&#xff09;二&#xff0c;路由安装和使用&#xff08;vue2&#xff09;三&#xff0c;路由跳转四&#xff0c;路由的传参和取值五&#xff0c;嵌套路由六&#xff0c;路由守卫最后 一&#xff0c;路由原理&#xff08;hash&#…...

中介者模式-协调多个对象之间的交互

在深圳租房市场&#xff0c;有着许多的“二房东”&#xff0c;房主委托他们将房子租出去&#xff0c;而租客想要租房的话&#xff0c;也是和“二房东”沟通&#xff0c;租房期间有任何问题&#xff0c;找二房东解决。对于房主来说&#xff0c;委托给“二房东”可太省事了&#…...

Python框架【自定义过滤器、自定义数据替换过滤器 、自定义时间过滤器、选择结构、选择练习、循环结构、循环练习、导入宏方式 】(三)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…...

红黑树遍历与Redis存储

引言 在计算机科学领域&#xff0c;红黑树&#xff08;Red-Black Tree&#xff09;是一种自平衡的二叉查找树&#xff0c;它能在O(log n)的时间复杂度内完成插入、删除和查找操作。由于其高效性和可预测性的性能&#xff0c;红黑树在许多领域都得到广泛应用。本文将重点介绍红…...

前端处理图片文件的方法

在项目开发过程中&#xff0c;有一个需求&#xff0c;需要前端对上传的图片进行处理&#xff0c;以字符串的形式传给后端&#xff0c;实现效果如下&#xff1a; 1.上传图片的组件 在该项目中&#xff0c;使用了element plus组件库 <el-uploadv-model:file-list"fileL…...

「Java」《深入解析Java多线程编程利器:CompletableFuture》

《深入解析Java多线程编程利器&#xff1a;CompletableFuture》 一、 引言1. 对多线程编程的需求和挑战的介绍2. 介绍CompletableFuture的作用和优势 二. CompletableFuture简介1. CompletableFuture是Java中提供的一个强大的多线程编程工具2. 与传统的Thread和Runnable相比的优…...

Docker容器与虚拟化技术:容器运行时说明与比较

目录 一、理论 1.容器运行时 2.容器运行时接口 3.容器运行时层级 4.容器运行时比较 5.强隔离容器 二、问题 1.K8S为何难以实现真正的多租户 三、总结 一、理论 1.容器运行时 &#xff08;1&#xff09;概念 Container Runtime 是运行于 k8s 集群每个节点中&#xff…...

vue导出文件流获取附件名称并下载(在response.headers里解析filename导出)

导出文件流下载&#xff0c;拦截器统一处理配置 需求以往实现的方法&#xff08;各自的业务层写方法&#xff09;现在实现的方法&#xff08;axios里拦截器统一配置处理&#xff09;把文章链接复制粘贴给后端&#xff0c;让大佬自己赏阅。 需求 之前实现的导出都是各自的业务层…...

​山东省图书馆典藏《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书

​山东省图书馆《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书...

2023-08-19力扣每日一题-水题/位运算解法

链接&#xff1a; 2235. 两整数相加 题意&#xff1a; ab 解&#xff1a; ab 补一个位运算写法&#xff0c;进位是(a&b)<<1&#xff0c;不进位的计算结果为a^b 实际代码&#xff1a; #include<iostream> using namespace std; int sum(int num1, int n…...

Hadoop学习:深入解析MapReduce的大数据魔力之数据压缩(四)

Hadoop学习&#xff1a;深入解析MapReduce的大数据魔力之数据压缩&#xff08;四&#xff09; 4.1 概述1&#xff09;压缩的好处和坏处2&#xff09;压缩原则 4.2 MR 支持的压缩编码4.3 压缩方式选择4.3.1 Gzip 压缩4.3.2 Bzip2 压缩4.3.3 Lzo 压缩4.3.4 Snappy 压缩4.3.5 压缩…...

LRU淘汰策略执行过程

1 介绍 Redis无论是惰性删除还是定期删除&#xff0c;都可能存在删除不尽的情况&#xff0c;无法删除完全&#xff0c;比如每次删除完过期的 key 还是超过 25%&#xff0c;且这些 key 再也不会被客户端访问。 这样的话&#xff0c;定期删除和堕性删除可能都彻底的清理掉。如果…...

Kotlin 高阶函数详解

高阶函数 在 Kotlin 中&#xff0c;函数是一等公民&#xff0c;高阶函数是 Kotlin 的一大难点&#xff0c;如果高阶函数不懂的话&#xff0c;那么要学习 Kotlin 中的协程、阅读 Kotlin 的源码是非常难的&#xff0c;因为源码中有太多高阶函数了。 高阶函数的定义 高阶函数的…...

网站开发建设准备工作/济南百度推广代理商

实验内容 本次实践项目就是将 Linux 0.11 中采用的 TSS 切换部分去掉&#xff0c;取而代之的是基于堆栈的切换程序。具体的说&#xff0c;就是将 Linux 0.11 中的 switch_to 实现去掉&#xff0c;写成一段基于堆栈切换的代码。 本次实验包括如下内容&#xff1a; 编写汇编程…...

网站开发外贸/怎么做优化关键词

2019独角兽企业重金招聘Python工程师标准>>> Laravel使用PHP的一个扩展API–Carbon来处理时间。它提供了很多日期操作方法&#xff0c;其中 diffForHumans() 可以把日期转换成 “1 hours age”、“4 years age” 这样的格式&#xff0c;同时&#xff0c;它还支持本地…...

wordpress自定义表/建站公司排名

logstash java 版本问题配置logstash收集应用日志时出现报错&#xff0c;说是找不到JAVA_HOME环境变量&#xff0c;但是明明已经设置了logstash要求java 1.8以上&#xff0c;查看生产环境&#xff1a;[rootlocalhost ~]# echo ${JAVA_HOME}/usr/local/jdk-12[rootlocalhost ~]#…...

代码网站模板/口碑营销经典案例

第5.1节 Fcitx 输入法框架 注意 在 FreeBSD-14.0-Current 中可能会出现许多不可预料的奇怪的 bug&#xff08;fcitx5 诊断信息英文乱码&#xff0c;输入法显示出奇怪的汉字&#xff0c;fcitx5-qt5 环境不能正常加载……&#xff09;&#xff0c;如果条件允许应该在 FreeBSD-Rel…...

做网站一定要用ps吗/站长之家ip地址归属查询

一、嵌入式linux字符设备驱动框架 写应用程序的人 不应该去看电路图&#xff0c;但是如何操作硬件呢&#xff1a;调用驱动程序里的open&#xff0c;read&#xff0c;write等来实现。 C库里实现了 open 、read、write上层函数 调用open等&#xff1a;swi val—引发一个异常中断…...

网站开发前景咋样/市场营销十大经典案例

一.推送简介转载于:https://www.cnblogs.com/erdeng/p/4901338.html...