当前位置:   article > 正文

flutter性能优化总结_flutter 性能优化

flutter 性能优化

Flutter应用程序默认已经具有良好的性能,因此您只需要避免常见的陷阱,就可以获得出色的性能。

流畅度(提高渲染性能)

控制build()方法的耗时

  • 避免在 build() 方法中进行重复且耗时的工作,因为当父 widget 重建时,子 Wdiget 的 build() 方法会被频繁地调用。

  • 将嵌套过多的 widget拆成不同的 widget,并进行封装

  • 在构建可复用的 UI 代码时,多使用 Widget 抽取组件,而不是函数。

  • 请尽可能地在 widget 上使用 const 构造函数

    const 在 Dart 中用于声明常量,应用到 widget 中就相当于告诉 Flutter,“我这个组件不会随状态更新而改变了。”,因此达到了减少重建的效果。

    使用 const 也需要注意如下几点:

    当const 修饰类的构造函数时,它要求该类的所有成员都必须是final的。
    const 变量只能在定义的时候初始化。

尽量减少 saveLayer 的调用(调用 saveLayer() 会开辟一片离屏缓冲区)

参考:Flutter 应用性能优化最佳实践 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

列表优化

  • 构建大型网格或列表的时候,使用懒加载的方式也就是使用 ListView 和 GridView 的 builder 方法,尽量避免使用 ListView(children: [],) 或 GridView(children: [],)
  • 对已知的所有单元格大小固定时设置属性:itemExtent

  • 针对于可折叠的 ListView,未展开状态时,设置其 itemCount 为 0,这样 item 只会在展开状态下才进行构建,以减少页面第一次的打开构建时间。

避免使用 Opacity widget,尤其是在动画中避免使用。可以使用 AnimatedOpacity 或 FadeInImage 代替该操作。

使用 AnimatedBuilder 时,将不需要变化的widget作为 child 传递给 AnimatedBuilder,从而只构建一次。

避免在动画中裁剪,尽可能的在动画开始之前预先裁剪图像。

实现局部刷新:

  • 通过抽取widget为StateFulWidget包裹,使用setState刷新制定widget
  • 使用StreamBuilder实现局部刷
  • Provide的解决方案设定顶级Widget,然后用consumer包裹子控件,调用更新,provider可以实现跨组件访问,(跨组件访问也可以通过context向上查询)
  • ValueListenableBuilder组件:可以监听一个值,当其变化时通过builder回调能重建界面,避免使用setState刷新
  • GlobalKey实现控件的局部刷新:将需要单独刷新的widget从复杂的布局中抽离出去,然后通过传GlobalKey引用,这样就可以通过GlobalKey实现跨组件的刷新了。
  • StatefulBuilder组件:需要传入builder属性进行构造组件,在build中可以使用StateSetter改变构造子组件的状态,即可以不用创建类而实现一个局部刷新的组件
  1. int a = 0;
  2. int b = 0;
  3. // 1、定义一个叫做“aState”的StateSetter类型方法;
  4. StateSetter? aState;
  5. @override
  6. Widget build(BuildContext context) {
  7. return Scaffold(
  8. body: Center(
  9. child: Column(
  10. mainAxisAlignment: MainAxisAlignment.center,
  11. children: <Widget>[
  12. // 2、将第一个“ElevatedButton”组件嵌套在“StatefulBuilder”组件内;
  13. StatefulBuilder(
  14. builder: (BuildContext context, StateSetter setState) {
  15. aState = setState;
  16. return ElevatedButton(
  17. onPressed: () {
  18. a++;
  19. // 3、调用“aState”方法对“StatefulBuilder”内部进行刷新;
  20. aState(() {});
  21. },
  22. child: Text('a : $a'),
  23. );
  24. },
  25. ),
  26. ElevatedButton(
  27. onPressed: () {
  28. b++;
  29. setState(() {});
  30. },
  31. child: Text('b : $b'),
  32. ),
  33. ],
  34. ),
  35. ),
  36. );
  37. }

合理使用Keys来加速Flutter性能

通过使用Keys,开发人员可以更精确地控制Flutter小部件树的重建过程,避免不必要的重建,提高应用程序的性能和响应性。

保留状态:使用GlobalKey作为Key的一种常见用法是在需要保留小部件状态的情况下。通过在重建时将相同的GlobalKey分配给相同类型的小部件,可以确保小部件在重建后保留其先前的状态,而不会丢失用户的输入或滚动位置。

  1. class MyWidget extends StatefulWidget {
  2. @override
  3. _MyWidgetState createState() => _MyWidgetState();
  4. }
  5. class _MyWidgetState extends State<MyWidget> {
  6. final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  7. @override
  8. Widget build(BuildContext context) {
  9. return Scaffold(
  10. key: _scaffoldKey,
  11. // Widget content
  12. );
  13. }
  14. }

列表中的重用:在ListViewGridView等可滚动列表中,使用Key可以帮助Flutter跟踪列表项并在数据源更改时有效地更新列表项,而无需重新创建整个列表。

  1. ListView.builder(
  2. itemCount: items.length,
  3. itemBuilder: (context, index) {
  4. return ListTile(
  5. key: Key(items[index].id.toString()),
  6. title: Text(items[index].title),
  7. );
  8. },
  9. )

动态添加或移除小部件:在动态添加或移除小部件时,使用Key可以帮助Flutter正确识别要添加或移除的小部件,而不会影响其他部分的布局。

  1. List<Widget> widgets = [
  2. Container(key: Key('1'), child: Text('Widget 1')),
  3. Container(key: Key('2'), child: Text('Widget 2')),
  4. ];
  5. // Add a new widget
  6. widgets.add(Container(key: Key('3'), child: Text('Widget 3')));
  7. // Remove a widget
  8. widgets.removeWhere((widget) => widget.key == Key('2'));

处理高消耗操作时用 isolates,但不要过度使用

释放你不用的内存数据

使用SKSL预热

如果一个应用在第一次运行时的动画不流畅,但后来相同的动画变得流畅,那很可能是由于着色器编译引起的不流畅。

flutter run --profile --cache-sksl --purge-persistent-cache

flutter build apk --cache-sksl --purge-persistent-cache

使用代码分析工具

代码分析工具,如Flutter分析器和Lint,对于提高代码质量和减少错误和漏洞的风险非常有帮助。这些工具可以帮助识别潜在问题,防止它们成为问题,并提供改进代码结构和可读性的建议。

flutter analyze lib/

使用 RepaintBoundary

RepaintBoundary是一个 Widget ,用于将其子部件的绘制内容分离为单独的绘制层。这样做的主要目的是减少不必要的重绘操作,提高应用程序的性能。当RepaintBoundary包裹一个子部件时,该子部件及其所有子部件将被视为一个整体,即使其中的其他部分发生重绘,RepaintBoundary内的内容也不会重绘。

RepaintBoundary的主要作用包括:

  1. 减少重绘范围:通过将子部件包裹在RepaintBoundary中,可以将其视为一个整体,仅在该部件内部发生重绘时才重新绘制,而不会影响到其他部分。

  2. 性能优化:避免不必要的重绘操作,可以提高应用程序的性能,特别是在具有复杂界面或动态内容的情况下。

  3. 避免全局重绘:在某些情况下,只需要更新特定部分的UI,而不是整个界面。通过使用RepaintBoundary,可以限制重绘的范围,避免全局重绘。

  4. 边界控制:可以通过RepaintBoundary来控制重绘的边界,确保只在需要时才进行重绘操作,而不会影响到其他部分。

RepaintBoundary是一个有用的工具,可以帮助优化Flutter应用程序的性能,特别是在需要控制重绘范围和避免不必要重绘操作的情况下。在开发复杂界面或需要动态更新的应用程序时,合理使用RepaintBoundary可以提高应用程序的性能和用户体验。

原生与flutter混合开发时flutter页面启动速度优化

启动速度优化最常用的方法是空间换时间法

方式一、通过flutter引擎预加载来达到页面秒开的效果:

  1. /**
  2. * 预加载flutter
  3. */
  4. fun preLoad(context: Context) {
  5. //在线程空闲时执行预加载任务
  6. Looper.myQueue().addIdleHandler {
  7. initFlutterEngine(context, MODULE_NAME_FAVORITE)
  8. initFlutterEngine(context, MODULE_NAME_RECOMMEND)
  9. false
  10. }
  11. }
  12. /**
  13. * 获取预加载的flutter
  14. */
  15. fun getCachedFlutterEngine(moduleName: String, context: Context?): FlutterEngine {
  16. var engine = FlutterEngineCache.getInstance()[moduleName]
  17. if (engine == null && context != null) {
  18. engine = initFlutterEngine(context, moduleName)
  19. }
  20. return engine!!
  21. }
  22. /**
  23. * 销毁flutterengine
  24. */
  25. fun destroyCached(moduleName: String) {
  26. FlutterEngineCache.getInstance()[moduleName]?.apply {
  27. destroy()
  28. }
  29. FlutterEngineCache.getInstance().remove(moduleName)
  30. }
  31. fun hastCached(moduleName: String): Boolean {
  32. return FlutterEngineCache.getInstance().contains(moduleName)
  33. }
  34. /**
  35. * 初始化flutter
  36. */
  37. private fun initFlutterEngine(context: Context, moduleName: String): FlutterEngine {
  38. val flutterEngine = FlutterEngine(context)
  39. flutterEngine.dartExecutor.executeDartEntrypoint(
  40. DartExecutor.DartEntrypoint(
  41. FlutterMain.findAppBundlePath(),
  42. moduleName
  43. )
  44. )
  45. FlutterEngineCache.getInstance().put(moduleName, flutterEngine)
  46. return flutterEngine
  47. }

方式二、预加载dartVM

  1. /**
  2. * 预加载DartVM
  3. */
  4. fun preLoadDartVM(context: Context) {
  5. val flutterLoader = FlutterLoader()
  6. val settings = FlutterLoader.Settings()
  7. flutterLoader.startInitialization(context, settings)
  8. val mainHandler = Handler(Looper.getMainLooper())
  9. flutterLoader.ensureInitializationCompleteAsync(
  10. context, arrayOf(), mainHandler
  11. ) {
  12. HiLog.i("flutter preLoadDartVM done")
  13. }
  14. }

两种方式都是有一定的内存成本的,对内存的消耗上:引擎预加载>DartVM预加载;对启动性能的提升上:引擎预加载>DartVM预加载,所以要不要空间换时间,还要看app具体情况。如果app内存压力不大,并且预判用户接下来会访问flutter业务,那使用这个优化就能带来很好的价值;反之,则可能造成资源浪费,意义不大。

应用程序的大小

执行如下命令,可以查看详细信息:

flutter build apk --analyze-size --target-platform android-arm64

  • 代码混淆
    flutter build apk --obfuscate --split-debug-info=./out/android/app.android-arm64.symbols
  • 资源文件优化

  • 避免使用过多的第三方库

  • 图片优化:1.压缩 PNG 和 JPEG 文件2.使用WebP格式替换PNG图片3.使用三方库压缩flutter_image_compress

  1. //https://pub.dev/packages/flutter_image_compress
  2. Future<Uint8List> testCompressFile(File file) async {
  3. var result = await FlutterImageCompress.compressWithFile(
  4. file.absolute.path,
  5. minWidth: 2300,
  6. minHeight: 1500,
  7. quality: 94,
  8. rotate: 90,
  9. );
  10. print(file.lengthSync());
  11. print(result.length);
  12. return result;
  13. }
  • 移除未使用的依赖库和资源。其中Android端

    1. buildTypes {
    2. release {
    3. // 移除无用的资源文件
    4. shrinkResources true
    5. // ZipAlign 优化
    6. zipAlignEnabled true
    7. // 设置混淆
    8. minifyEnabled true
    9. // Signing with the debug keys for now, so `flutter run --release` works.
    10. signingConfig signingConfigs.release
    11. }
    12. }

    minifyEnabled:是否启用代码缩减
    如果将 minifyEnabled 属性设为 true,系统会默认启用 R8 代码缩减功能。代码缩减(也称为“摇树优化”)是指移除 R8 确定在运行时不需要的代码的过程。此过程可以大大减小应用的大小,例如,当您的应用包含许多库依赖项,但只使用它们的一小部分功能时。
    shrinkResources:是否启用缩减资源
    资源缩减只有在与代码缩减配合使用时才能发挥作用。在代码缩减器移除所有不使用的代码后,资源缩减器便可确定应用仍要使用的资源。
     

  • android端可以设置cpu架构

    1. ndk {
    2. // armeabi:已经淘汰(0%)
    3. // armeabi-v7a:曾经主流的架构平台(20%)
    4. // arm64-v8a:目前主流架构平台(80%)
    5. abiFilters "armeabi-v7a", "arm64-v8a"
    6. // abiFilters "arm64-v8a"
    7. }

       

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/962269
推荐阅读
相关标签
  

闽ICP备14008679号