与其余混合平台相比, Flutter 性可能快吗?答案是必定的,然而出于这种思考,让咱们来看看一些令人惊叹的性能和优化实际。

原文

https://inficial.medium.com/f...

参考

  • https://api.flutter.dev/flutt...
  • https://blog.codemagic.io/how...
  • https://pub.dev/packages/nil
  • https://medium.com/flutter-co...
  • https://dart.dev/guides/langu...
  • https://dart-lang.github.io/l...
  • https://itnext.io/comparing-d...
  • https://iisprey.medium.com/ge...

注释

1. 应用 Widgets 代替函数

不要这样用它

Widget _buildFooterWidget() {  return Padding(            padding: const EdgeInsets.all(8.0),            child: Text('This is the footer '),         );}.

像这样应用它

class FooterWidget extends StatelessWidget {  @override   Widget build(BuildContext context) {  return Padding(            padding: const EdgeInsets.all(8.0),            child: Text('This is the footer '),         );      }}

如果一个函数能够做同样的事件,Flutter 就不会有 StatelessWidget。

相似地,它次要是针对公共 widgets 的,这些 widgets 能够重复使用。私人函数只能应用一次并不重要ーー只管意识到这种行为依然是好的。

应用函数而不是类之间有一个重要的区别,那就是: 框架不晓得函数,然而能够看到类。

思考上面的“ widget”函数:

Widget functionWidget({ Widget child}) {    return Container(child: child);}

这样应用:

functionWidget(    child : functionWidget())

而且它是等价的:

class ClassWidget extends StatelessWidget {final Widget child;const ClassWidget({Key key, this.child}) : super(key: key); @override Widget build(BuildContext context) {      return Container(               child: child,              );  }}

这样应用:

new ClassWidget(  child : new ClassWidget(),)

在纸面上,两者仿佛做了同样的事件: 创立 2 个容器,一个嵌套在另一个中。但现实情况略有不同。

对于函数,生成的 widgets 树如下所示:

Container  Container

对于类,widgets 树是:

ClassWidget  Container    ClassWidget      Container

这一点很重要,因为它扭转了框架在更新 widgets 时的行为。

为什么这很重要

通过应用函数将 widgets 树拆分为多个 widgets,您将本人裸露在 bug 之中,并错过了一些性能优化。

不能保障应用函数会呈现 bug,然而应用类能够保障不会遇到这些问题。
the issues:

上面是一些在 Dartpad 上的交互式例子,你能够本人跑步来更好地了解这些问题:

  • https://dartpad.dev/1870e726d... 这个例子展现了如何通过宰割你的应用程序的性能,你可能会不小心毁坏诸如 AnimatedSwitcher 之类的货色
  • https://dartpad.dev/a869b21a2...
    这个例子展现了类如何容许更细粒度地从新构建 widgets 树,从而进步性能
  • https://dartpad.dev/06842ae9e...
    这个例子展现了在应用 InheritedWidgets (比方主题或提供程序)时,如何通过应用函数使本人裸露在谬误应用 BuildContext 和谬误背后

总的来说,因为这些起因,在类上应用函数来重用 widgets 被认为是一种不好的做法。你能够,但它可能会在将来咬你。

  • 防止反复重建所有 widgets
  • 而且,增加 const 是个好主见。
  • ListView 长列表的用户我的项目范畴。

指定 itemExtent 比让子滚动机制确定其范畴更无效,因为滚动机制能够利用子滚动机制的事后常识来节俭工作,例如当滚动地位产生激烈变动时。

  • 防止在 animatedBuilder 中重建不必要的 widgets

不要这样用它

body: AnimatedBuilder(    animation: _controller,    builder: (_, child) => Transform(        alignment: Alignment.center,        transform: Matrix4.identity()          ..setEntry(3, 2, 0.001)          ..rotateY(360 * _controller.value * (pi / 180.0)),        child: CounterWidget(                 counter: counter,               ),),),

这将在呈现动画时从新构建 CounterWidget widgets。如果您将 log 放入 counterWidget 的 build 办法,那么您能够看到每次呈现动画时它都会打印一个日志。

像这样应用它。

body: AnimatedBuilder(   animation: _controller,   child : CounterWidget(       counter: counter,    ),    builder: (_, child) => Transform(        alignment: Alignment.center,        transform: Matrix4.identity()          ..setEntry(3, 2, 0.001)          ..rotateY(360 * _controller.value * (pi / 180.0)),        child: child),),

咱们应用 AnimatedBuilder 提供的子属性,它容许咱们缓存 widgets 以在动画中重用它们。咱们这样做是因为这个 widgets 不会扭转,它惟一要做的就是旋转,然而为此,咱们有了 Transform widgets。

查看更多信息:

https://blog.codemagic.io/how...

2. 尽可能应用 const

class CustomWidget extends StatelessWidget {   const CustomWidget(); @override  Widget build(BuildContext context) {    ...  }}
  • 在构建 widgets 时,或者应用 flutter widgets 时。这有助于 flutter 只重建应该更新的 widgets。当 setState 调用时,widgets 不会更改。它将阻止 widgets 从新构建。
  • 您能够节俭 CPU 周期,并将它们与 const 构造函数一起应用。

3. 用 nil 代替 const Container ()

// goodtext != null ? Text(text) : const Container()// Bettertext != null ? Text(text) : const SizedBox()// BESTtext != null ? Text(text) : nilorif (text != null) Text(text)

当您不想显示任何内容时,在 widgets 树中增加一个简略的 widgets,对性能的影响最小。

https://pub.dev/packages/nil

4. 用 keys 进步 Flutter 性能

// FROMreturn value   ? const SizedBox()   : const Placeholder(),// TOreturn value   ? const SizedBox(key: ValueKey('SizedBox'))   : const Placeholder(key: ValueKey('Placeholder')),----------------------------------------------// FROMfinal inner = SizedBox();return value ? SizedBox(child: inner) : inner,// TOfinal global = GlobalKey();final inner = SizedBox(key: global);return value ? SizedBox(child: inner) : inner,

通过应用键的抖动更好地辨认 widgets,这提供了更好的性能。

5. 应用图像 ListView 时优化内存

ListView.builder(  ...   addAutomaticKeepAlives: false (true by default)   addRepaintBoundaries: false (true by default));

ListView 无奈杀死它的子节点,因为它们在屏幕上不可见。如果儿童有高分辨率的图像,会耗费大量的内存。

做这些选项谬误,可能导致应用更多的 GPU 和 CPU 工作,但它能够解决咱们的内存问题,您将失去一个十分高性能的认识没有显著的问题。

Flutter 记忆优化系列

简直在 Flutter 的任何货色都是默认优化和加强的,正如你可能曾经晓得的那样,感激 Flutter 团队在..

https://medium.com/flutter-co...

6. 遵循 Dart 格调

Identifiers:: 标识符有三种格调

  • UpperCamelCase 名称将每个单词的首字母大写,包含第一个单词。
  • lowerCamelCase 每个单词的第一个字母都大写,除了第一个字母总是小写,即便它是首字母缩略词。
  • lowercase*with_underscores 名称只应用小写字母,即便对于首字母缩写也是如此,而应用 \_ 的单词也是如此。

应用 UpperCamelCase 命名类型

类、枚举类型、 typedef 和类型参数应该大写每个单词(包含第一个单词)的第一个字母,并且不应用分隔符。

goodclass SliderMenu { ... }class HttpRequest { ... }typedef Predicate<T> = bool Function(T value);const foo = Foo();@fooclass C { ... }

应用以下命名库、包、目录和源文件

Goodlibrary peg_parser.source_scanner;import 'file_system.dart';import 'slider_menu.dart';Badlibrary pegparser.SourceScanner;import 'file-system.dart';import 'SliderMenu.dart';

要理解更多样式,请查看 Dart 款式:

好的代码的一个惊人的重要局部是好的格调。统一的命名、排序和格局有助于代码..

https://dart.dev/guides/langu...

要理解更多规定,请查看 Dart 连接器:

https://dart-lang.github.io/l...

7. 只在须要的时候应用 MediaQuery/LayoutBuilder

8. 应用 async/await 代替 then 函数

9. 无效应用操作符

var car = van == null ? bus : audi;         // Old patternvar car = audi ?? bus;                      // New patternvar car = van == null ? null : audi.bus;    // Old patternvar car = audi?.bus;                        // New pattern(item as Car).name = 'Mustang';         // Old patternif (item is Car) item.name = 'Mustang'; // New pattern

10. 利用字符串模板内插

// Inappropriatevar discountText = 'Hello, ' + name + '! You have won a brand new ' + brand.name + 'voucher! Please enter your email to redeem. The offer expires within ' + timeRemaining.toString() ' minutes.';// Appropriatevar discountText = 'Hello, $name! You have won a brand new ${brand.name} voucher! Please enter your email to redeem. The offer expires within ${timeRemaining} minutes.';

11. 应用 for/while 代替 foreach/map

您能够在本文中查看循环的比拟

比拟 Dart 的线圈ーー哪一个最快?

编写 Flutter apps 的语言 Dart 有许多不同的循环,它们能够循环遍历列表或运行一些..

https://itnext.io/comparing-d...

12. 准确显示你的图片和图标

precacheImage(AssetImage(imagePath), context);For SVGsyou need flutter_svg package.precachePicture(  ExactAssetPicture(    SvgPicture.svgStringDecoderBuilder,iconPath),context,);

13. 应用 SKSL Warmup

flutter run --profile --cache-sksl --purge-persistent-cacheflutter build apk --cache-sksl --purge-persistent-cache

如果一个应用程序在第一次运行时有简洁的动画,之后对于同样的动画变得晦涩,那么这很可能是因为着色器编译的提早。

14. 思考应用 RepaintBoundary widgets

Flutter widgets 与渲染对象相关联。渲染对象有一个名为 paint 的办法,用于执行绘制。然而,即便关联的 widgets 实例不变,也能够调用 paint 办法。这是因为如果其中一个被标记为脏的话,Flutter 可能会对同一层中的其余渲染对象执行从新绘制。当渲染对象须要通过 RenderObject.markneedspaint 从新绘制时,它会通知它最近的先人从新绘制。先人对其先人执行雷同的操作,可能直到根 RenderObject 为止。当一个渲染对象的 paint 办法被触发时,它在同一层中的所有子渲染对象都将被从新绘制。

在某些状况下,当须要从新绘制渲染对象时,同一层中的其余渲染对象不须要从新绘制,因为它们出现的内容放弃不变。换句话说,如果咱们只能从新绘制某些渲染对象会更好。应用 RepaintBoundary 对于限度 markneedspain 在渲染树上的流传和 paintChild 在渲染树上的流传十分有用。RepaintBoundary 能够将先人出现对象与后辈出现对象解耦。因而,只能从新绘制内容发生变化的子树。应用 RepaintBoundary 能够显著进步应用程序的性能,特地是如果不须要从新绘制的子树须要大量的从新绘制工作时。

15. 如果可能的话,应用命名结构器

// 例如Listview → Listview.builder

16. 适当解决数据

不必要的内存应用会在应用程序内悄悄地杀死数据,所以不要遗记解决你的数据

有些软件包为它们的类提供主动开释反对

  • Riverpod
  • GetX
  • get_it
  • flutter_hooks

如果你不晓得什么是 Flutter 挂钩和如何应用它

用 Flutter 钩子去除各种样板代码

您不认为,当初是时候敞开 StatefulWidget 并应用 flutter hook 删除样板代码了吗

https://iisprey.medium.com/ge...

将 cacheHeight 和 cacheWidth 设置为图像

您能够通过这种形式缩小内存应用

17. 不要在 List Map 中应用援用

不要这样应用

List a = [1,2,3,4];List b;b = a;a.remove(1);print(a);  // [2,3,4]print(b);  // [2,3,4]

因为这个起因,每当您尝试调用列表 a 的任何办法时,都会主动调用列表 b。

比方 a.remove (some) ; 也会从列表 b 中删除该项;

像这样应用它

List a = [1,2,3,4];List b;b = jsonDecode(jsonEncode(a));a.remove(1);print(a);  // [2,3,4]print(b);  // [1,2,3,4]

end

我心愿这给你一些见解,以改善您的 Flutter 应用程序的性能。高兴的编码!


© 猫哥

  • 微信 ducafecat
  • 博客 ducafecat.tech
  • github
  • bilibili