flutter性能優(yōu)化實踐

知識點記錄

  • 滾動列表,child不在可視區(qū)域內(nèi)是不會paint的。
//class RenderSliverMultiBoxAdaptor
@override
  void paint(PaintingContext context, Offset offset) {
  ...
    while (child != null) {
...
      // If the child's visible interval (mainAxisDelta, mainAxisDelta + paintExtentOf(child))
      // does not intersect the paint extent interval (0, constraints.remainingPaintExtent), it's hidden.
      if (mainAxisDelta < constraints.remainingPaintExtent && mainAxisDelta + paintExtentOf(child) > 0)
        context.paintChild(child, childOffset);

      child = childAfter(child);
    }
  }
  • 內(nèi)存壓力監(jiān)聽。通過WidgetsBinding可以監(jiān)聽內(nèi)存壓力事件,從而進(jìn)行內(nèi)存清理操作
//class WidgetsBinding
void addObserver(WidgetsBindingObserver observer) => _observers.add(observer);
bool removeObserver(WidgetsBindingObserver observer) => _observers.remove(observer);

@override
  void handleMemoryPressure() {
    super.handleMemoryPressure();
    for (final WidgetsBindingObserver observer in _observers)
      observer.didHaveMemoryPressure();
  }

列表數(shù)據(jù)緩存和預(yù)加載

詳情見:flutter 列表數(shù)據(jù)緩存和預(yù)加載

分幀layout和paint

dart pub

  • DelayBuildWidget控制builder返回的widget分幀創(chuàng)建。
  • DelayLayoutAndPaintWidget控制child的layout和paint分幀進(jìn)行。
  • 如果isScrollalbeItem 參數(shù)為true,則通過Scrollable.recommendDeferredLoadingForContext(context)判斷當(dāng)前小部件是否滾動太快了,如果滾動太快則在下一幀再判斷是否build、layou和paint

例如:假設(shè)多個DelayBuildWidget的buildManager相同,則每幀進(jìn)行一個DelayBuildWidget的builder調(diào)用并顯示builder返回的widget。 DelayLayoutAndPaintWidget的原理類似,不過分幀處理的是child的layout和paint。

使用該封裝前后性能對比如下:

以下截圖為列表按設(shè)定速度滾動得到的cpu、gpu圖,由兩圖可以看出優(yōu)化后cpu性能明顯將一幀的高耗時分?jǐn)偟蕉鄠€幀上了


沒優(yōu)化的

使用分幀優(yōu)化的

圓角圖片

dart pub
不使用ClipRRect顯示圓角圖片,而是在ImageProvider中生成帶圓角的圖片在遞交Image顯示

用法

Image(
    image: RoundCornersImageProvider.asset(
        'assets/icon_round_corners.png',
        cornerRadius: 30,
        imageShowSize: Size(60, 60),
        cornerColor: Colors.yellow
    ),
    width: 60,
    height: 60,
),

相對于系統(tǒng)提供的ImageProvider增加的屬性及其作用

///圓角,如果imageShowSize不為空,則通過計算使得以imageShowSize顯示出來的圖片圓角為cornerRadius,
  ///如果imageShowSize為空,而cacheImageWidth或者cacheImageHeight不為空,則將resize之后的圖片圓角設(shè)置為cornerRadius
  ///如果imageShowSize、cacheImageWidth、cacheImageHeight都為空,則將原圖圓角設(shè)置為cornerRadius
  final int cornerRadius;
  //cornerRadius圓角外圍部分的顏色,則被裁剪掉部分顏色
  final Color cornerColor;
  //ImageCache緩存圖片的寬高設(shè)置
  final int cacheImageWidth;
  final int cacheImageHeight;

  //imageShowSize設(shè)置后裁取的位置
  //如果設(shè)置ClipLocation.Start,則當(dāng)原始圖片過長的時候從頭部(上或左)截取寬高比為imageShowSize框高比的圖片。
  final ClipLocation clipLocation;

  //圖片顯示的大小。如果設(shè)置了圖片顯示寬高,會按圖片顯示寬高比截取原圖。圓角也會匹配imageShowSize
  final Size imageShowSize;

效果

以下截圖gpu、cup數(shù)據(jù)為不斷刷新頁面得到的性能圖。由兩圖對比可看出通過ImageProvider生成圓角圖片可以提高cpu和gup性能


使用ClipRRect

ImageProvider生成圓角

自定義layout

如:文字左邊顯示與文字等高的豎線

普通做法為使用TextPainter先layou獲取字體高度,在將高度設(shè)置給豎線。
此處可以通過自定義layout,在performLayout方法中先layout字體,然后得到高度,再使用字體高度進(jìn)行豎線的layou。主要代碼如下:
完整例子

///自定義Row的RenderObject的performLayout方法
@override
  void performLayout() {
    final BoxConstraints constraints = this.constraints;
    assert(constraints != null);

    WithIDRenderObject lineChild;
    WithIDRenderObject textChild;
    RenderBox child = firstChild;
    while (child != null) {
      if (child is WithIDRenderObject) {
        if (child.uid == 'line') {
          lineChild = child;
        } else if (child.uid == 'text') {
          textChild = child;
        }
      }
      final FlexParentData childParentData = child.parentData as FlexParentData;
      child = childParentData.nextSibling;
    }

    ///layout
    textChild.layout(BoxConstraints(maxWidth: constraints.maxWidth - 28), parentUsesSize: true);
    lineChild.layout(BoxConstraints(minWidth: 0, maxWidth: 28, maxHeight: textChild.size.height), parentUsesSize: true);

    ///設(shè)置this的size
    size = Size(constraints.maxWidth, textChild.size.height);

    ///設(shè)置child.parentData.offset
    final FlexParentData leftChildParentData = lineChild.parentData as FlexParentData;
    leftChildParentData.offset = Offset(0, 0);
    final FlexParentData rightChildParentData = textChild.parentData as FlexParentData;
    rightChildParentData.offset = Offset(lineChild.size.width, 0);
  }

注:CustomMultiChildLayout不能根據(jù)子控件大小設(shè)置父控件大小,所以不用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容