知識點記錄
- 滾動列表,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
- 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è)置父控件大小,所以不用。