Flutter 性能優(yōu)化匯總工作中用到的點

官方文檔中比較淺顯的一些
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');

  • 使用 const Widget 作為子節(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.builderSliverList 這兩種方式只構(gòu)建可見區(qū)域的 Widget,避免一次性構(gòu)建大量 Widget[參考資料缺失]。

  • 使用 AutomaticKeepAliveClientMixin 對于 PageViewTabBarView 中的頁面,可以使用 AutomaticKeepAliveClientMixin 來保持頁面的狀態(tài),避免每次切換時都重新構(gòu)建[參考資料缺失]。

  • b. 優(yōu)化圖片加載

  • 使用 CachedNetworkImage CachedNetworkImage 庫可以緩存網(wǎng)絡(luò)圖片,避免重復(fù)加載[參考資料缺失]。

  • 使用占位符: 在圖片加載完成之前,顯示一個占位符,避免 UI 閃爍[參考資料缺失]。

  • 預(yù)加載圖片: 在滾動開始之前,預(yù)先加載一部分圖片,減少滾動過程中的加載延遲[參考資料缺失]。

  • 調(diào)整圖片大小: 加載適合屏幕尺寸的圖片,避免加載過大的圖片[參考資料缺失]。

  • c. 異步處理耗時操作

  • 使用 FutureBuilderStreamBuilder 將網(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í)行,例如 initStatedidChangeDependencies 或事件處理函數(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 樹重建范圍和頻次

  1. const 使用原則:

    // ? 正確:使用 const 構(gòu)造函數(shù)
    const ListTile(title: const Text('Title'));
    
    // ? 錯誤:所有元素都不帶 const
    ListTile(title: Text('Title'));
    

    原理const 組件會被編譯為編譯期常量,有效減少重建時的對象創(chuàng)建開銷

  2. 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)存管理

核心原則:合理控制對象生命周期

  1. 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;
      }
    }
    
  2. 圖片緩存策略:

    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

  1. 基準優(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
    )
    
  2. 分頁加載優(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ù)量

  1. 重繪隔離技巧:

    • 動態(tài)判斷是否添加隔離層:
    Widget build(BuildContext context) {
      final needsBoundary = hasAnimations || hasFrequentUpdates;
      return needsBoundary 
          ? RepaintBoundary(child: _realContent())
          : _realContent();
    }
    
  2. 圖層合并優(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ù)雜場景)

  1. 按需渲染模式

    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]),
                );
              },
            );
          },
        );
      }
    }
    
  2. 內(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)化四步法則

  1. :每次修改后用 devtools 性能面板驗證
  2. :通過時間線定位具體問題(構(gòu)建、布局、繪制、合成)
  3. 優(yōu):針對性應(yīng)用上述優(yōu)化方案
  4. :權(quán)衡優(yōu)化效果與復(fù)雜度,避免過度優(yōu)化

記?。?strong>用真機測不要用假機測原因是模擬器有些很大的差距 ??

?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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