本文介绍了10个每个Flutter开发者都应该掌握的性能优化技巧,帮助你提升应用的运行效率和用户体验。

10个Flutter性能优化技巧

10个Flutter性能优化技巧

视频

前言

原文 每个开发者都应掌握的10个Flutter性能优化技巧

本文介绍了10个每个Flutter开发者都应该掌握的性能优化技巧,帮助你提升应用的运行效率和用户体验。

正文

1. 尽可能使用 const Widgets

在 Flutter 中,尽可能使用 const Widgets 的原因主要包括以下几点:

性能优化

  • 减少重建const Widgets 在编译时创建并缓存,避免了在每次构建时重复创建相同的 Widget。这可以显著减少不必要的重建,提升应用性能。
  • 节省资源:由于不需要在每次更新时重新构造,使用 const 可以减少内存使用和 CPU 负担。

提高可读性

  • 代码清晰:标记为 const 的 Widgets 明确表示它们的状态在整个生命周期内不会改变,有助于提高代码的可读性和可维护性。

优化树结构

  • 树的稳定性:使用 const 可以使 Widget 树在构建时更稳定,Flutter 能更有效地管理 Widget 树,减少对 UI 更新的影响。

避免不必要的比较

  • 引用相等const Widgets 是唯一的实例,因此在比较时只需检查引用而非内容。这使得 Flutter 在更新 Widget 树时可以更高效地判断是否需要重绘。

使用 const 的形式:

const Text('Hello, World!');

不使用 const 的形式:

Text('Hello, World!');

Flutter 中的小部件是无状态且不可变的。实际上,当你使用“const”关键字时,你就是在告诉 Flutter 该小部件的属性不会随着时间改变。这有助于 Flutter 在重建应用程序的过程中渲染相同的部件而不是创建新的部件,从而使其更快。

如何实现:

const Text('Hello World') // Using const constructor

为什么它很重要: 通过使用“const”小部件,由于 Flutter 能够更好地管理小部件树,因此重建时占用的内存更少,耗时更短。

2. 使用 setState 最小化重建

要在 Flutter 中实现 setState 的最小化更新,可以遵循以下步骤:

  • 局部更新
    • 将状态管理限制在需要更新的子组件中,避免整个父组件重建。
  • 条件更新
    • setState 前检查状态变化,只有在状态确实变化时才调用 setState()
  • 简化 Widget 树
    • 尽量减少 Widget 树的深度和复杂度,使得重建时的性能影响最小。
  • 拆分组件
    • 将大组件拆分为多个小组件,使得只重建需要更新的部分。
  • 使用 const 构造函数
    • 对于不需要更新的子组件使用 const,避免不必要的重建。

通过这些方法,可以有效地减少 setState 带来的重建范围,从而提高性能。

3. 利用 RepaintBoundary 的强大功能

RepaintBoundary 是 Flutter 中的一个 widget,主要用于优化绘制性能。它的作用是将子树的绘制与其他部分分离,从而减少重绘区域。以下是 RepaintBoundary 的使用方法和特点:

使用方法

  • 包裹需要优化的组件: 将需要单独处理绘制的部分包裹在 RepaintBoundary 内。
    class MyWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Text('This part will be redrawn normally'),
            RepaintBoundary(
              child: Container(
                color: Colors.blue,
                width: 100,
                height: 100,
                child: Center(child: Text('Optimized')),
              ),
            ),
            Text('This part will also be redrawn normally'),
          ],
        );
      }
    }
    
  • 使用 GlobalKey: 可以通过 GlobalKey 获取 RepaintBoundary 的绘制信息,如图像。
    final GlobalKey _key = GlobalKey();
    
    void _capturePng() async {
      RenderRepaintBoundary boundary = _key.currentContext.findRenderObject();
      var image = await boundary.toImage();
      // 处理图像
    }
    

特点

  • 绘制优化RepaintBoundary 会将其子树的绘制与其它部分分离,只有子树改变时才会重绘,减少不必要的重绘。
  • 性能提升: 对于复杂的 UI,使用 RepaintBoundary 可以显著提高性能,特别是在动画或频繁更新的情况下。
  • 图像捕捉: 可以通过 RepaintBoundary 捕获其绘制的内容为图像,方便用于分享或保存。
  • 易于使用: 只需简单地包裹需要优化的 widget,便可实现性能提升。

通过使用 RepaintBoundary,Flutter 开发者可以更有效地管理绘制性能,特别是在需要频繁更新 UI 的场景中。

4. 避免使用透明度来控制控件的可见性/不可见性

使用 Opacity 小部件会消耗较多资源,因为它会为小部件应用一层透明效果。相反,应使用“Visibility”或条件渲染(“if”语句)来显示或隐藏小部件,以不影响性能。

相反,请使用:

Opacity(opacity: 0.0, child: MyWidget())

使用:

Visibility(visible: false, child: MyWidget())

或者有条件地:

if (isVisible) MyWidget()

为什么它很重要:这个 hack 防止了框架不必要的继续渲染隐藏的小部件。

5. 对于大型列表使用 ListView.builder

对于包含大量项目的列表,始终使用“ListView.builder”而不是“ListView”。“ListView.builder”仅构建屏幕上可见的项目,而“ListView”会一次性构建所有项目,这可能会导致性能问题。

示例:

ListView.builder(
  itemCount: 1000,
  itemBuilder: (context, index) {
    return ListTile(title: Text('Item $index'));
  },
)

为什么它很重要: 这避免了内存过载,并通过按需懒加载项目来确保滚动顺畅。

6. 对于重载任务使用 Async/Await

将 CPU 密集型任务卸载到后台线程使用“async”和“await”,而不是阻塞主线程。像 API 调用、文件 I/O 或数据库访问等操作永远不应该阻塞 UI 线程。

示例:

Future<void> loadData() async {
  var data = await fetchDataFromAPI();
  setState(() {
    _data = data;
  });
}

为什么它重要: 这确保了在后台处理繁重任务时,您的用户界面保持响应状态。

7. 使用 cached_network_image 缓存图片:

从网络加载和渲染图像可能会很慢并且资源消耗大。“cached_network_image”包会将图像缓存到本地,减少再次获取的需要,提高滚动性能。

如何实现:

CachedNetworkImage(
  imageUrl: "https://example.com/image.png",
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
)

为什么这很重要: 通过缓存图像,您的应用减少了网络请求和内存使用,从而实现更快的图像渲染和更流畅的滚动。

8. 减少使用大型单个组件

处理多个关注点的大型小部件会降低性能。将它们拆分为较小的、独立的小部件。这确保了仅重新构建需要更新的 UI 部分。

而不是一个庞大的小部件:

Widget build(BuildContext context) {
  return Column(
    children: [
      buildHeader(),
      buildBody(),
      buildFooter(),
    ],
  );
}

分解它:

class Header extends StatelessWidget { ... }
class Body extends StatelessWidget { ... }
class Footer extends StatelessWidget { ... }

为什么这很重要: 这种模块化方法确保了仅重建受影响的部件,减少了不必要的计算。

9. 使用 Flutter DevTools 对您的应用进行性能分析

Flutter DevTools 提供了强大的性能分析工具,可让您监控应用程序的性能,包括渲染性能和内存使用情况。通过定期对应用程序进行性能分析,您可以发现性能瓶颈、慢帧和内存泄漏。

如何实现:

  • 以调试模式运行您的应用。
  • 打开终端并运行:
flutter pub global activate devtools
flutter run --track-widget-creation

当开启这个选项时,Flutter 会在构建 Widget 时记录创建这些 Widget 的源代码位置。这使得在调试时能够更容易地识别和定位 Widget 的定义位置。

  • 使用 Dart DevTools 界面分析你的应用。

为什么它很重要: 此工具通过提供实时性能统计信息来帮助识别应用程序中需要优化的部分。

10. 谨慎使用 Skia Shader Masking

在 Flutter 中,使用 Skia Shader Masking 需要谨慎,主要原因如下:

性能开销

  • 复杂性:Shader Masking 在绘制过程中涉及更复杂的图形处理,可能导致性能下降,尤其是在动画或高频率重绘的场景中。
  • GPU 负担:Shader 操作通常会消耗更多的 GPU 资源,增加渲染负担,可能导致帧率下降。

渲染问题

  • 兼容性:不同设备和平台上的 GPU 可能表现不同,Shader 的效果可能在某些设备上无法如预期工作,导致跨平台一致性问题。
  • 错误渲染:在某些情况下,Shader 可能会导致渲染错误,如不正确的边缘处理或图形失真。

调试困难

  • 难以追踪的问题:使用 Shader Masking 可能会使调试变得更加复杂,难以定位性能瓶颈或渲染错误。

替代方案

  • 简单方法:在很多情况下,可以使用更简单的绘制方法或组件实现相似效果,而不必使用 Shader Masking,从而避免可能的性能问题。

示例

ShaderMask(
  shaderCallback: (Rect bounds) {
    return RadialGradient(
      center: Alignment.topLeft,
      radius: 1.0,
      colors: <Color>[Colors.yellow, Colors.deepOrange.shade900],
      tileMode: TileMode.mirror,
    ).createShader(bounds);
  },
  child: Text('Shader Masked Text'),
)

为什么它很重要: 误用着色器遮罩可能会导致性能显著下降,特别是在较旧或配置较低的设备上。

小结

在本文中,我们分享了10个每个Flutter开发者都应该了解的性能优化技巧。这些技巧不仅可以帮助开发者提升应用的运行效率,还能显著改善用户体验。通过合理的性能优化,开发者能够确保Flutter应用在不同设备上流畅运行,满足用户的期望。如果你希望在Flutter开发中取得更好的成绩,不妨参考这些实用的优化策略。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


猫哥 APP

flutter 学习路径


© 猫哥 ducafecat.com

end