官方文檔中比較淺顯的一些
1.謹慎使用 saveLayer()
- ShaderMask
- ColorFilter
- Chip— 當(dāng) disabledColorAlpha != 0xff 的時候,會調(diào)用 saveLayer()
- Text— 當(dāng)有 overflowShader 時,會調(diào)用saveLayer()
2.Opacity 別用 方案 :
Image.network(
'https://raw.githubusercontent.com/flutter/assets-for-api-docs/main/packages/diagrams/assets/blend_mode_destination.jpeg',
color: const Color.fromRGBO(255, 255, 255, 0.5),
colorBlendMode: BlendMode.modulate
)
要在圖像中實現(xiàn)淡入淡出,請考慮使用 FadeInImage widget
- Clipping 不會調(diào)用 saveLayer() (除非明確使用 Clip.antiAliasWithSaveLayer),因此這些操作沒有 Opacity 那么耗時,但仍然很耗時,所以請謹慎使用。
- 加載動畫使用骨骼動畫? shadermask
https://docs.flutter.cn/cookbook/effects/shimmer-loading
3.animator 中千萬別在vsync值監(jiān)聽中去setstate
4.如果大多數(shù) children widget 在屏幕上不可見,請避免使用返回具體列表的構(gòu)造函數(shù)(例如 Column() 或 ListView()),以避免構(gòu)建成本。
避免在 Widget 對象上重寫 operator ==。雖然這看起來有助于避免不必要的重建,但在實踐中,它實際上損害了性能,因為這是 O(N2) 的行為。只有 leaf widget(沒有子的 widget)是個例外,在這種特殊的情況下,比較 widget 的屬性可能比重建 widget 更加有效,也能更少改變 widget 的配置。即使在這種情況下,最好還要緩存 widget,因為哪怕有一次對 operator == 進行覆蓋也會導(dǎo)致全面性能的下降,編譯器也會因此不再認為調(diào)用總是靜態(tài)的。
5.監(jiān)聽的指標 Raster Build UI線程 jank 包含
要避免 Flutter 中的 Scroll Jank(滾動卡頓),可以從以下幾個方面入手,結(jié)合具體場景進行優(yōu)化:
1. 理解 Scroll Jank 的根源
幀率不穩(wěn)定: 滾動流暢需要穩(wěn)定的 60 FPS(或更高的刷新率)。任何導(dǎo)致幀率下降的操作都可能引起卡頓[參考資料缺失]。
復(fù)雜的構(gòu)建/渲染: 在滾動過程中,如果 Flutter 需要進行大量的 Widget 構(gòu)建或復(fù)雜的渲染計算,就會占用主線程時間,導(dǎo)致掉幀[參考資料缺失]。
資源加載延遲: 滾動時加載圖片、網(wǎng)絡(luò)數(shù)據(jù)等資源,如果加載速度慢,會阻塞 UI 線程[參考資料缺失]。
2. 優(yōu)化策略
a. 減少 Widget 重建
使用
const關(guān)鍵字: 對于靜態(tài)的、不變的 Widget,使用const關(guān)鍵字可以避免不必要的重建[參考資料缺失]。
const Text('This is a static text');
使用
constWidget 作為子節(jié)點: 如果 Widget 的子節(jié)點是const的,那么當(dāng)父 Widget rebuild 時,子節(jié)點可以跳過 rebuild[參考資料缺失]。使用
Key: 當(dāng)列表中的 Widget 順序發(fā)生變化時,F(xiàn)lutter 默認會嘗試復(fù)用現(xiàn)有的 Widget。但如果 Widget 的內(nèi)容也發(fā)生了變化,就會導(dǎo)致不必要的 rebuild。使用Key可以幫助 Flutter 正確地識別和復(fù)用 Widget[參考資料缺失]。使用
ListView.builder或SliverList: 這兩種方式只構(gòu)建可見區(qū)域的 Widget,避免一次性構(gòu)建大量 Widget[參考資料缺失]。使用
AutomaticKeepAliveClientMixin: 對于PageView或TabBarView中的頁面,可以使用AutomaticKeepAliveClientMixin來保持頁面的狀態(tài),避免每次切換時都重新構(gòu)建[參考資料缺失]。b. 優(yōu)化圖片加載
使用
CachedNetworkImage:CachedNetworkImage庫可以緩存網(wǎng)絡(luò)圖片,避免重復(fù)加載[參考資料缺失]。使用占位符: 在圖片加載完成之前,顯示一個占位符,避免 UI 閃爍[參考資料缺失]。
預(yù)加載圖片: 在滾動開始之前,預(yù)先加載一部分圖片,減少滾動過程中的加載延遲[參考資料缺失]。
調(diào)整圖片大小: 加載適合屏幕尺寸的圖片,避免加載過大的圖片[參考資料缺失]。
c. 異步處理耗時操作
使用
FutureBuilder或StreamBuilder: 將網(wǎng)絡(luò)請求、數(shù)據(jù)庫查詢等耗時操作放在異步任務(wù)中執(zhí)行,避免阻塞 UI 線程[參考資料缺失]。使用
compute函數(shù): 對于 CPU 密集型任務(wù)(例如復(fù)雜的數(shù)學(xué)計算),可以使用compute函數(shù)在后臺線程中執(zhí)行[參考資料缺失]。d. 避免在 Build 方法中執(zhí)行耗時操作
Build 方法應(yīng)該只負責(zé)構(gòu)建 UI,避免在其中執(zhí)行任何耗時操作。耗時操作應(yīng)該放在其他地方執(zhí)行,例如
initState、didChangeDependencies或事件處理函數(shù)中[參考資料缺失]。e. 減少 Overdraw
Overdraw 指的是在同一像素上繪制多次。過多的 Overdraw 會浪費 GPU 資源,導(dǎo)致性能下降。可以使用 Flutter DevTools 中的 Overdraw 可視化工具來檢測 Overdraw 情況,并進行優(yōu)化[參考資料缺失]。
f. 優(yōu)化自定義繪制
如果使用了
CustomPaint進行自定義繪制,需要確保繪制邏輯是高效的。避免在paint方法中進行復(fù)雜的計算或資源加載[參考資料缺失]。可以使用
shouldRepaint方法來控制是否需要重新繪制。只有當(dāng)繪制內(nèi)容發(fā)生變化時,才需要重新繪制[參考資料缺失]。g. 使用 Profiler 進行性能分析
Flutter DevTools 提供了強大的 Profiler 工具,可以幫助你分析應(yīng)用的性能瓶頸。通過 Profiler,你可以找到導(dǎo)致卡頓的具體原因,并進行針對性的優(yōu)化[參考資料缺失]。
h. 啟用 Impeller 渲染引擎
Impeller 是 Flutter 新的渲染引擎,它通過預(yù)編譯著色器等技術(shù),可以提高渲染性能,減少卡頓。目前 Impeller 在 iOS 上默認啟用,Android 上還在開發(fā)中[參考資料缺失]。
3. 代碼示例
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
class MyList extends StatelessWidget {
final List<String> imageUrls;
MyList({Key? key, required this.imageUrls}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: imageUrls.length,
itemBuilder: (context, index) {
return Card(
child: Row(
children: [
CachedNetworkImage(
imageUrl: imageUrls[index],
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
width: 100,
height: 100,
fit: BoxFit.cover,
),
SizedBox(width: 16),
Expanded(
child: Text('Image ${index + 1}'),
),
],
),
);
},
);
}
}
總結(jié)
避免 Scroll Jank 需要綜合考慮多個因素,包括減少 Widget 重建、優(yōu)化圖片加載、異步處理耗時操作、減少 Overdraw 等。通過使用 Flutter DevTools 進行性能分析,可以幫助你找到具體的瓶頸,并進行針對性的優(yōu)化。 記住,針對不同的場景,優(yōu)化的側(cè)重點也會有所不同。
一、構(gòu)建優(yōu)化(Build 階段優(yōu)化)
核心原則:減少 Widget 樹重建范圍和頻次
-
const使用原則:// ? 正確:使用 const 構(gòu)造函數(shù) const ListTile(title: const Text('Title')); // ? 錯誤:所有元素都不帶 const ListTile(title: Text('Title'));原理:
const組件會被編譯為編譯期常量,有效減少重建時的對象創(chuàng)建開銷 -
Widget 拆分標準:
- 業(yè)務(wù)邏輯分離:邏輯操作與布局代碼隔離
- 頻繁更新的部分獨立成子Widget(如動畫元素)
- 示例:
// 拆分前:整個列表項在父組件中構(gòu)建 itemBuilder: (ctx, i) => ListTile( leading: Image.network(data[i].url), title: Text(data[i].title), subtitle: _buildComplexContent(), ) // 拆分后:獨立組件優(yōu)化 itemBuilder: (ctx, i) => ListItem(data[i]) class ListItem extends StatelessWidget { final Data data; const ListItem(this.data); @override Widget build(BuildContext context) { return ListTile( leading: OptimizedImage(data.url), title: Text(data.title), subtitle: ContentWidget(data.content), ); } }
二、內(nèi)存管理
核心原則:合理控制對象生命周期
-
ListView 優(yōu)化鐵律:
- 禁用
addAutomaticKeepAlives(分頁加載場景) - 動態(tài)保持關(guān)鍵狀態(tài):
// 自定義條件保留狀態(tài)組件 class ConditionalKeepAlive extends StatefulWidget { final bool keepAlive; final Widget child; const ConditionalKeepAlive({ required this.keepAlive, required this.child }); @override _ConditionalKeepAliveState createState() => _ConditionalKeepAliveState(); } class _ConditionalKeepAliveState extends State<ConditionalKeepAlive> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => widget.keepAlive; @override Widget build(BuildContext context) { super.build(context); return widget.child; } } - 禁用
-
圖片緩存策略:
CachedNetworkImage( imageUrl: url, memCacheWidth: (MediaQuery.of(context).size.width * 2).toInt(), // 物理像素適配 maxWidthDiskCache: 1024, // 服務(wù)器端大圖時限制本地存儲尺寸 filterQuality: FilterQuality.low, // 降低渲染質(zhì)量節(jié)省GPU資源 )
三、滾動性能優(yōu)化(List/Grid 核心優(yōu)化點)
核心指標:滾動幀率穩(wěn)定在 60fps
-
基準優(yōu)化方案:
ListView.builder( itemCount: 1000, itemExtent: 80, // ? 顯式設(shè)定行高(避免動態(tài)計算) cacheExtent: 500, // ? 預(yù)加載區(qū)域調(diào)整 addAutomaticKeepAlives: false, // ??禁用自動保持 addRepaintBoundaries: true, // ? 智能添加重繪隔離 physics: const BouncingScrollPhysics(), // ??低端機用ClampingScrollPhysics ) -
分頁加載優(yōu)化公式:
加載公式:觸發(fā)閾值 = 最大滾動距離 - 當(dāng)前偏移 公式實現(xiàn): if (scrollOffset >= maxScrollExtent - triggerThreshold) { loadNextPage(); }代碼實現(xiàn):
void _scrollListener() { final max = _controller.position.maxScrollExtent; final current = _controller.position.pixels; if (current >= max - 200) { // 提前200像素加載 _loadData(); } }
四、渲染管線優(yōu)化
核心原理:減少 GPU 合成層數(shù)量
-
重繪隔離技巧:
- 動態(tài)判斷是否添加隔離層:
Widget build(BuildContext context) { final needsBoundary = hasAnimations || hasFrequentUpdates; return needsBoundary ? RepaintBoundary(child: _realContent()) : _realContent(); } -
圖層合并優(yōu)化:
// ? 正確:層級扁平化 Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [/*...*/], ), ) // ? 錯誤:過度嵌套 Container( color: Colors.white, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Container( decoration: BoxShadow(...), ), ), )
五、關(guān)鍵性能指標及調(diào)優(yōu)工具
診斷指標對照表:
| 指標 | 健康范圍 | 危險信號 | 優(yōu)化措施 |
|---|---|---|---|
| FPS (幀率) | ≥58 fps | <45 fps | 檢查渲染管線 |
| UI 線程耗時 | <16ms | >30ms | 優(yōu)化build方法 |
| GPU 線程耗時 | <8ms | >12ms | 減少圖層合成 |
| 對象創(chuàng)建數(shù)/幀 | <1000 | >2000 | 檢查對象復(fù)用 |
| Scroll jank | 0-2次/滾動 | >5次/滾動 | 列表項優(yōu)化 |
工具鏈使用指南:
# 性能分析專用構(gòu)建命令
flutter run --profile --purge-persistent-cache
# 調(diào)試包分析
flutter build apk --analyze-size
flutter build appbundle --target-platform android-arm64 --analyze-size
六、進階性能模式(針對復(fù)雜場景)
-
按需渲染模式:
class SmartListView extends StatefulWidget { @override _SmartListViewState createState() => _SmartListViewState(); } class _SmartListViewState extends State<SmartListView> { final _visibleIndexes = <int>{}; void _handleScroll(ScrollMetrics metrics) { final first = metrics.minScrollExtent; final last = metrics.maxScrollExtent; final newIndexes = calculateVisibleItems(first, last); if (newIndexes != _visibleIndexes) { setState(() => _visibleIndexes = newIndexes); } } @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: DataModel.itemsNotifier, builder: (ctx, items, _) { return ListView.builder( itemCount: items.length, itemBuilder: (ctx, index) { return VisibilityTracker( visible: _visibleIndexes.contains(index), child: ListItem(item: items[index]), ); }, ); }, ); } } -
內(nèi)存壓縮方案:
// 圖片內(nèi)存優(yōu)化組件 class CompressedImage extends StatelessWidget { final String url; const CompressedImage(this.url); @override Widget build(BuildContext context) { return Image.network( url, cacheWidth: MediaQuery.of(context).size.width.toInt() * 2, filterQuality: FilterQuality.low, loadingBuilder: (ctx, child, progress) { return progress == null ? child : Shimmer.fromColors(...); // 內(nèi)存友好的加載動畫 }, ); } }
結(jié)語:性能優(yōu)化四步法則
-
測:每次修改后用
devtools性能面板驗證 - 斷:通過時間線定位具體問題(構(gòu)建、布局、繪制、合成)
- 優(yōu):針對性應(yīng)用上述優(yōu)化方案
- 衡:權(quán)衡優(yōu)化效果與復(fù)雜度,避免過度優(yōu)化
記?。?strong>用真機測不要用假機測原因是模擬器有些很大的差距 ??