Flutter性能揭秘之RepaintBoundary

这篇具有很好参考价值的文章主要介绍了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);

  @override
  void 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)
      );
    }
  }

  @override
  bool 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);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawCircle(
      _offset,
      10.0,
      new Paint()..color = Colors.green,
    );
  }

  @override
  bool 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 {

  @override
  State 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(),
        ),
      ),
    );
  }

  @override
  Widget 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文章来源地址https://www.toymoban.com/news/detail-667607.html

到了这里,关于Flutter性能揭秘之RepaintBoundary的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络奇遇记】揭秘计算机网络性能指标:全面指南

    🌈个人主页: 聆风吟 🔥系列专栏: 网络奇遇记、数据结构 🔖少年有梦不应止于心动,更要付诸行动。     计算机网络的性能指标是用来衡量和评估网络的各种性能方面的指标。常用的有 速率 、 带宽 、 吞吐量 、 时延 、 时延带宽积 、 往返时间 、 利用率 及 丢包

    2024年01月22日
    浏览(46)
  • Go协程揭秘:轻量、并发与性能的完美结合

    Go协程为并发编程提供了强大的工具,结合轻量级、高效的特点,为开发者带来了独特的编程体验。本文深入探讨了Go协程的基本原理、同步机制、高级用法及其性能与最佳实践,旨在为读者提供全面、深入的理解和应用指导。 关注公众号【TechLeadCloud】,分享互联网架构、云

    2024年02月08日
    浏览(38)
  • Redis 7.0性能大揭秘:如何优化缓存命中率?

    Redis 7.0,这货不仅仅是一个简单的缓存工具,它更是一款高性能的数据结构服务器。现在,大家都知道缓存命中率对性能影响特别大,但怎么优化它呢? 本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享 Redis的数据结构和键的

    2024年02月03日
    浏览(48)
  • 轻松做性能测试,月入3万的主流测试工具大揭秘

    在为大家介绍性能测试工具以前,先让我们一起回顾一下什么是性能测试。 1、为什么需要性能测试? 举个例子。下图是一张交通图。图中的车流类似于性能测试中的数据,图中的车道、十字路口就相当于数据流量的规则和约束。 正常情况下,车流按照规则指示行驶,那么,

    2024年02月03日
    浏览(40)
  • 解锁MySQL性能瓶颈!超实用的10种优化方法大揭秘

    💡一个热爱分享高性能服务器后台开发知识的博主,目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。技能涵盖了多个领域,包括C/C++、Linux、Nginx、MySQL、Redis、fastdfs、kafka、Docker、TCP/IP、协程、DPDK等。 👉 🎖️ CSDN实力新星,CSDN博客专

    2024年02月03日
    浏览(42)
  • 可观测性革命 - 揭秘OpenObserve开源高性能云原生平台

      OpenObserve简介 OpenObserve  是一个开源的云原生可观测性平台,与 Elasticsearch 相比,存储成本降低了约 140 倍(实际结果可能因测试数据而有所不同),测试用例包括真实的日志数据,其显著降低运营成本,并提高了易用性。它可以扩展到PB级别的数据量,具有很高的性能,您

    2024年02月11日
    浏览(38)
  • flutter-移动端适配

    不同屏幕之间的尺寸适配 使用插件 flutter_screenutil flutter 屏幕适配方案,用于调整屏幕和字体大小的flutter插件,让你的UI在不同尺寸的屏幕上都能显示合理的布局! 安装 # add flutter_screenutil flutter_screenutil: ^5.8.4

    2024年02月12日
    浏览(31)
  • Redis为什么能抗住10万并发?揭秘性能优越的背后原因

    Redis是一个开源的,基于内存的,高性能的键值型数据库。它支持多种数据结构,包含五种基本类型 String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),和三种特殊类型 Geo(地理位置)、HyperLogLog(基数统计)、Bitmaps(位图),可以满足各种应用场

    2023年04月13日
    浏览(53)
  • 【网络奇遇记】揭秘计算机网络的性能指标:速率|带宽|吞吐量|时延

    🌈个人主页: 聆风吟 🔥系列专栏: 网络奇遇记、数据结构 🔖少年有梦不应止于心动,更要付诸行动。     计算机网络的性能指标是用来衡量和评估网络的各种性能方面的指标。常用的有 速率 、 带宽 、 吞吐量 、 时延 、 时延带宽积 、 往返时间 、 利用率 及 丢包

    2024年02月04日
    浏览(45)
  • 提升Spring Boot应用性能的秘密武器:揭秘@Async注解的实用技巧

    在日常业务开发中,异步编程已成为应对并发挑战和提升应用程序性能的关键策略。传统的同步编程方式,由于会阻碍主线程执行后续任务直至程序代码执行结束,不可避免地降低了程序整体效率与响应速度。因此,为克服这一瓶颈,开发者广泛采用异步编程技术,将那些可

    2024年03月11日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包