Flutter 應(yīng)用程序性能優(yōu)化建議

視頻

https://www.bilibili.com/video/BV1ht421L7mP/

前言

原文 https://ducafecat.com/blog/boosting-flutter-performance-top-tips-for-developers

Flutter應(yīng)用程序默認(rèn)已經(jīng)具有良好的性能,因此您只需要避免常見的陷阱,就可以獲得出色的性能。

您設(shè)計和實現(xiàn)應(yīng)用程序的用戶界面的方式可能會對其運行效率產(chǎn)生重大影響。

本文這些最佳實踐建議將幫助您編寫性能最佳的Flutter應(yīng)用程序。

那么讓我們開始吧!

正文

代碼結(jié)構(gòu)拆分合理

干凈架構(gòu)

細(xì)致的拆分

https://marketplace.visualstudio.com/items?itemName=ducafecat.getx-template

使用狀態(tài)管理

需要一套規(guī)范來耦合所有的內(nèi)容

常見的優(yōu)秀狀態(tài)管理有:

  • provider
  • bloc
  • getx
  • riverpod

可以看下各種狀態(tài)管理文章 https://ducafecat.com/blog/flutter-state-management-libraries-2024

使用代碼分析工具

代碼分析工具,如Flutter分析器和Lint,對于提高代碼質(zhì)量和減少錯誤和漏洞的風(fēng)險非常有幫助。這些工具可以幫助識別潛在問題,防止它們成為問題,并提供改進代碼結(jié)構(gòu)和可讀性的建議。

flutter analyze lib/

使用 Flutter Inspector 進行調(diào)試

flutter run --debug
圖片.png

之前錄過一個 dev tools 性能調(diào)優(yōu)的視頻

https://www.bilibili.com/video/BV1Tb4y1p7t9

https://ducafecat.tech/2022/03/17/2022/flutter-devtools-performance/

懶加載和分頁

一次獲取和渲染大量數(shù)據(jù)可能會顯著影響性能。實現(xiàn)延遲加載和分頁,根據(jù)需要加載數(shù)據(jù),特別是對于長列表或數(shù)據(jù)密集的視圖。

ListView.builder 方式

List<Item> loadItems(int pageNumber) {
}

ListView.builder(
  itemCount: totalPages,
  itemBuilder: (context, index) {
    return FutureBuilder(
      future: loadItems(index),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          // Build your list item here.
        } else {
          return CircularProgressIndicator();
        }
      },
    );
  },
);

pull_to_refresh_flutter 方式

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SmartRefresher(
        enablePullDown: true,
        enablePullUp: true,
        header: WaterDropHeader(),
        footer: CustomFooter(
          builder: (BuildContext context,LoadStatus mode){
            Widget body ;
            if(mode==LoadStatus.idle){
              body =  Text("pull up load");
            }
            else if(mode==LoadStatus.loading){
              body =  CupertinoActivityIndicator();
            }
            else if(mode == LoadStatus.failed){
              body = Text("Load Failed!Click retry!");
            }
            else if(mode == LoadStatus.canLoading){
                body = Text("release to load more");
            }
            else{
              body = Text("No more Data");
            }
            return Container(
              height: 55.0,
              child: Center(child:body),
            );
          },
        ),
        controller: _refreshController,
        onRefresh: _onRefresh, // 下拉刷新
        onLoading: _onLoading, // 上拉載入
        child: ListView.builder(
          itemBuilder: (c, i) => Card(child: Center(child: Text(items[i]))),
          itemExtent: 100.0,
          itemCount: items.length,
        ),
      ),
    );
  }

https://pub-web.flutter-io.cn/packages/pull_to_refresh_flutter3

壓縮圖片

你獲取到一張寬 40000 的圖片,如果你直接打印,真的噩夢了。

你需要本地壓縮后再顯示 flutter_image_compress。

https://pub.dev/packages/flutter_image_compress

  Future<Uint8List> testCompressFile(File file) async {
    var result = await FlutterImageCompress.compressWithFile(
      file.absolute.path,
      minWidth: 2300,
      minHeight: 1500,
      quality: 94,
      rotate: 90,
    );
    print(file.lengthSync());
    print(result.length);
    return result;
  }

優(yōu)化動畫

避免使用對應(yīng)用程序性能產(chǎn)生影響的繁重或復(fù)雜的動畫,尤其是在舊設(shè)備上。謹(jǐn)慎使用動畫,并考慮使用Flutter內(nèi)置的動畫,如 AnimatedContainer , AnimatedOpacity 等。

// 1 秒太長了
AnimatedContainer(
  duration: Duration(seconds: 1),
  height: _isExpanded ? 300 : 1000,
  color: Colors.blue,
);

// 縮短動畫時長
AnimatedContainer(
  duration: Duration(milliseconds: 500),
  height: _isExpanded ? 300 : 100,
  color: Colors.blue,
);

優(yōu)化應(yīng)用程序啟動時間

通過優(yōu)化初始化過程來減少應(yīng)用程序的啟動時間。使用 flutter_native_splash 包在應(yīng)用程序加載時顯示啟動畫面,并延遲非必要組件的初始化直到應(yīng)用程序啟動后。

https://pub.dev/packages/flutter_native_splash

多些組件抽取

不要去寫層次很深的代碼, 多些代碼抽取。

  // 主視圖
  Widget _buildView() {
    List<Widget> ws = [];
    
    // 標(biāo)題
    if (title != null) {
      ws.add(_buildTitle(title!));
    }

    // 統(tǒng)計欄
    ws.add(_buildTotalBar(win, draw, lose, winAvg, loseAvg));

    // 視圖
    for (var item in fixtures) {
      // 欄
      if (item.league?.id != lastLeagueId) {
        lastLeagueId = item.league?.id ?? 0;
        ws.add(_buildLeagueBar(lastLeagueId, item.league?.name ?? ""));
      }

      // 行
      ws.add(_buildRow(item));

      // 分隔符
      ws.add(Container(
        height: 0.5,
        color: AppColors.surfaceVariant,
      ));
    }

    return ws.toColumn();
  }

使用級聯(lián)(..)

如果你剛開始使用Flutter,你可能還沒有使用過這個運算符,但當(dāng)你想在同一個對象上執(zhí)行某些任務(wù)時,它非常有用。

//Bad
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

//Good
var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;

使用擴展運算符(...)

其它語言也有,可以用來合并集合,只是下面的用法太神奇。

// 很啰嗦
@override
Widget build(BuildContext context) {
  bool isTrue = true;
  return Scaffold(
    body: Column(
      children: [
        isTrue ? const Text('One') : Container(),
        isTrue ? const Text('Two') : Container(),
        isTrue ? const Text('Three') : Container(),
      ],
    ),
  );
}

// 才知道可以這樣用 ... 符號
@override
Widget build(BuildContext context) {
  bool isTrue = true;
  return Scaffold(
    body: Column(
      children: [
        if(isTrue)...[
          const Text('One'),
          const Text('Two'),
          const Text('Three')
        ]
      ],
    ),
  );
}

抽取你的樣式定義

// 繁瑣
Column(
  children: const [
    Text(
      'One',
      style: TextStyle(
        fontSize: 14,
        fontWeight: FontWeight.normal,
      ),
    ),
    Text(
      'Two',
      style: TextStyle(
        fontSize: 14,
        fontWeight: FontWeight.normal,
      ),
    ),
  ],
)

// 復(fù)用 重構(gòu)
Column(
  children: [
    Text(
      'One',
      style: Theme.of(context).textTheme.subtitle1,
    ),
    Text(
      'Two',
      style: Theme.of(context).textTheme.subtitle1,
    ),
  ],
),

局部刷新

StatefulBuilder 方式

 int a = 0;
 int b = 0;

 // 1、定義一個叫做“aState”的StateSetter類型方法;
 StateSetter? aState;

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
           // 2、將第一個“ElevatedButton”組件嵌套在“StatefulBuilder”組件內(nèi);
           StatefulBuilder(
             builder: (BuildContext context, StateSetter setState) {
               aState = setState;
               return ElevatedButton(
                 onPressed: () {
                   a++;
                   // 3、調(diào)用“aState”方法對“StatefulBuilder”內(nèi)部進行刷新;
                   aState(() {});
                 },
                 child: Text('a : $a'),
               );
             },
           ),
           ElevatedButton(
             onPressed: () {
               b++;
               setState(() {});
             },
             child: Text('b : $b'),
           ),
         ],
       ),
     ),
   );
 }

也可以用 getx GetBuilder 這種狀態(tài)組件實現(xiàn)局部刷新。

定義 GetBuilder,設(shè)置 id 名稱

  @override
  Widget build(BuildContext context) {
    return GetBuilder<HomeIndexController>(
      init: HomeIndexController(),
      id: "home_index",
      builder: (_) {
        return Scaffold(
          appBar: appBarWidget(...),
          body: _buildView(),
        );
      },
    );
  }

控制器觸發(fā), 制定 id 名稱,可以是一個列表

update(["home_index"]);

多使用 Widget 抽取組件,而不是函數(shù)

您可以節(jié)省CPU周期,并使用const構(gòu)造函數(shù),在僅在需要時進行重建,并獲得更多的好處(例如重用等)。

// 定義成 widget
@override
Widget build(BuildContext context) {
  return Column(
    children: [
      HeaderWidget(), 
      SubHeaderWidget(), 
      ContentWidget()
    ]
  );
}

使用 final

使用 final 關(guān)鍵字可以極大地提高您的應(yīng)用程序的性能。當(dāng)一個值被聲明為 final 時,它只能被設(shè)置一次,之后不會再改變。這意味著框架不需要不斷地檢查變化,從而提高了性能。

  final String tag;
  final Color? color;
  final Size? size;
  final double? radius;
  final Color? fontColor;

使用 const

如果已經(jīng)定義了,您可以使用相同的 Widget 來節(jié)省RAM。 const widgets 在編譯時創(chuàng)建,因此在運行時更快。

x = const Container(); 
y = const Container(); 

使用 const 類構(gòu)造函數(shù)

這有助于 Flutter 僅重新構(gòu)建應(yīng)更新的 Widget。

class FbTagWidget extends StatelessWidget {
  const FbTagWidget(this.tag,
      {super.key, this.color, this.size, this.radius, this.fontColor});

盡可能使用 private 關(guān)鍵詞

這更像是 Dart 的最佳實踐,而不是性能。

但是,最佳實踐可以在某種程度上提高性能,比如理解代碼,減少復(fù)雜性等等。

class Student{
  String _name;
  String _address;
  Student({
    required String name,
    required String address,
  }): 
  _name = name,
  _address = address;
}

使用nil代替const Container()

零消耗

// 原來
text != null ? Text(text) : const Container()

// 后來
text != null ? Text(text) : const SizedBox()

// 現(xiàn)在
text != null ? Text(text) : nil 

在ListView中使用itemExtent來處理長列表

這有助于Flutter計算滾動位置,而不是計算每個 Widget 的高度,并使?jié)L動動畫更加高效。

默認(rèn)情況下,每個子項都必須確定其范圍,這在性能方面是非常昂貴的。顯式設(shè)置值可以節(jié)省大量的CPU周期。列表越長,使用此屬性可以獲得更多的速度提升。

final List<int> _listItems = <int>[1, 2, 3, 4, 5, 6, 7, 8, 9];

@override
Widget build(BuildContext context) {
  return ListView.builder(
    itemExtent: 150,
    itemCount: _listItems.length, 
    itemBuilder: (context, index) {
      var item = _listItems[index];
      return Center(
        child: Text(item.toString())
      );
    }
}

避免在 setState 中使用 AnimationController

錯誤的方式,將 addListener 去掉。

void initState() {
  _controller = AnimationController(
    vsync: this,
    duration: Duration(seconds: 1), 
  )..addListener(() => setState(() {})); 
}

Column( 
  children: [
    Placeholder(), // rebuilds 
    Placeholder(), // rebuilds 
    Placeholder(), // rebuilds 
    Transform.translate( // rebuilds
      offset: Offset(100 * _controller.value, 0),
      child: Placeholder(), 
    ),
  ], 
),

使用Keys來加速Flutter性能

在Flutter中,使用Keys可以幫助加速性能并優(yōu)化應(yīng)用程序的重建過程。Keys在Flutter中有多種用途,其中一項重要的功能是幫助Flutter識別小部件樹中的特定小部件,從而在進行重建時更有效地更新小部件。以下是一些示例,說明如何使用Keys來加速Flutter性能:

保留狀態(tài):使用GlobalKey作為Key的一種常見用法是在需要保留小部件狀態(tài)的情況下。通過在重建時將相同的GlobalKey分配給相同類型的小部件,可以確保小部件在重建后保留其先前的狀態(tài),而不會丟失用戶的輸入或滾動位置。

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      // Widget content
    );
  }
}

列表中的重用:在ListViewGridView等可滾動列表中,使用Key可以幫助Flutter跟蹤列表項并在數(shù)據(jù)源更改時有效地更新列表項,而無需重新創(chuàng)建整個列表。

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      key: Key(items[index].id.toString()),
      title: Text(items[index].title),
    );
  },
)

動態(tài)添加或移除小部件:在動態(tài)添加或移除小部件時,使用Key可以幫助Flutter正確識別要添加或移除的小部件,而不會影響其他部分的布局。

List<Widget> widgets = [
  Container(key: Key('1'), child: Text('Widget 1')),
  Container(key: Key('2'), child: Text('Widget 2')),
];

// Add a new widget
widgets.add(Container(key: Key('3'), child: Text('Widget 3')));

// Remove a widget
widgets.removeWhere((widget) => widget.key == Key('2'));

通過使用Keys,開發(fā)人員可以更精確地控制Flutter小部件樹的重建過程,避免不必要的重建,提高應(yīng)用程序的性能和響應(yīng)性。

使用 ListView 列表視圖時優(yōu)化內(nèi)存

ListView.builder( 
  ...
  addAutomaticKeepAlives: false (true by default)
  addRepaintBoundaries: false (true by default) 
);
  • addAutomaticKeepAlives 當(dāng)這個屬性設(shè)置為true時,F(xiàn)lutter會嘗試在滾動列表時保留列表項的狀態(tài)。這意味著即使列表項在屏幕外被移除,它們的狀態(tài)仍然會被保留,以便在滾回到它們時可以保持其狀態(tài)。
  • addRepaintBoundaries 當(dāng)這個屬性設(shè)置為true時,F(xiàn)lutter會嘗試在列表項之間創(chuàng)建重繪邊界。這意味著在滾動列表時,只有在需要時才會重繪列表項,而不是每次滾動都重繪所有內(nèi)容。

使用 for/while 代替 foreach/map

如果你要處理大量的數(shù)據(jù),使用正確的循環(huán)可能會對你的性能產(chǎn)生影響。

預(yù)緩存您的圖片和圖標(biāo)

圖片

precacheImage(
  AssetImage(imagePath), 
  context
);

svg

precachePicture( 
  ExactAssetPicture(SvgPicture.svgStringDecoderBuilder, iconPath), 
  context
);

使用SKSL預(yù)熱

如果一個應(yīng)用在第一次運行時的動畫不流暢,但后來相同的動畫變得流暢,那很可能是由于著色器編譯引起的不流暢。

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

使用 RepaintBoundary

RepaintBoundary是一個 Widget ,用于將其子部件的繪制內(nèi)容分離為單獨的繪制層。這樣做的主要目的是減少不必要的重繪操作,提高應(yīng)用程序的性能。當(dāng)RepaintBoundary包裹一個子部件時,該子部件及其所有子部件將被視為一個整體,即使其中的其他部分發(fā)生重繪,RepaintBoundary內(nèi)的內(nèi)容也不會重繪。

RepaintBoundary的主要作用包括:

  1. 減少重繪范圍:通過將子部件包裹在RepaintBoundary中,可以將其視為一個整體,僅在該部件內(nèi)部發(fā)生重繪時才重新繪制,而不會影響到其他部分。

  2. 性能優(yōu)化:避免不必要的重繪操作,可以提高應(yīng)用程序的性能,特別是在具有復(fù)雜界面或動態(tài)內(nèi)容的情況下。

  3. 避免全局重繪:在某些情況下,只需要更新特定部分的UI,而不是整個界面。通過使用RepaintBoundary,可以限制重繪的范圍,避免全局重繪。

  4. 邊界控制:可以通過RepaintBoundary來控制重繪的邊界,確保只在需要時才進行重繪操作,而不會影響到其他部分。

RepaintBoundary是一個有用的工具,可以幫助優(yōu)化Flutter應(yīng)用程序的性能,特別是在需要控制重繪范圍和避免不必要重繪操作的情況下。在開發(fā)復(fù)雜界面或需要動態(tài)更新的應(yīng)用程序時,合理使用RepaintBoundary可以提高應(yīng)用程序的性能和用戶體驗。

class RepaintBoundaryExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text('This is inside RepaintBoundary'),
          SizedBox(height: 20),
          CustomPaint(
            size: Size(200, 200),
            painter: MyPainter(),
          ),
        ],
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 5
      ..style = PaintingStyle.stroke;

    canvas.drawRect(Rect.fromLTWH(50, 50, 100, 100), paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

使用 Listview.builder

Listview.builder()

用了之后不出現(xiàn)在屏幕上的元素,不渲染。

不要使用 ShrinkWrap 來包裹可滾動 Widget

ShrinkWrap 動態(tài)確定子組件大小。

包裹滾動組件后可能有布局錯誤、性能問題。

處理高消耗操作時用 isolates

比如處理非常大的 json 文件、視頻壓縮。

這樣不會卡主線程。

可以用一些 Dart 包簡化代碼。

https://pub-web.flutter-io.cn/packages/flutter_isolate

不要過度使用 isolates

如果你在每個最小的操作中都使用 isolates,你的應(yīng)用程序可能會非??D。

這是因為生成一個 isolates 并不是一項廉價的操作。它需要時間和資源。

釋放你不用的內(nèi)存數(shù)據(jù)

比如你載入一個圖片數(shù)據(jù)進行加工,如加文字、加二維碼,不用的時候請釋放。

壓縮數(shù)據(jù)處理

為了節(jié)省內(nèi)存,請壓縮您的數(shù)據(jù)。

比如你載入了百兆的 json 文件,你可以壓縮起來放在內(nèi)存中。

final response = await rootBundle.loadString('assets/en_us.json');

final original = utf8.encode(response); 

final compressed = gzip.encode(original); 
final decompress = gzip.decode(compressed);

final enUS = utf8.decode(decompress);

保持 Flutter 新穩(wěn)定版本

在每個版本中,F(xiàn)lutter都變得越來越快。

所以不要忘記及時更新你的Flutter版本,并繼續(xù)創(chuàng)作出令人驚艷的作品!

注意用穩(wěn)定版。

https://docs.flutter.dev/release/archive?tab=macos

請多準(zhǔn)備幾臺真機調(diào)試

始終在真實設(shè)備上測試您的應(yīng)用程序性能,包括較舊的型號,以便發(fā)現(xiàn)在模擬器或較新設(shè)備上可能不明顯的性能問題。

使用StatelessWidget而不是StatefulWidget

一個 StatelessWidget 比一個 StatefulWidget 更快,因為它不需要像其名稱所暗示的那樣管理狀態(tài)。

所以如果可能的話,你應(yīng)該優(yōu)先選擇它。

不要使用OpacityWidget

Opacity Widget在與動畫一起使用時可能會導(dǎo)致性能問題,因為 Opacity Widget的所有子Widget都會在每個新幀中重新構(gòu)建。在這種情況下,最好使用 AnimatedOpacity 。如果您想要淡入一張圖片,請使用FadeInImageWidget。如果您想要具有不透明度的顏色,請繪制具有不透明度的顏色。

//不推薦
Opacity(opacity: 0.5, child: Container(color: Colors.red))

//推薦
Container(color: Color.fromRGBO(255, 0, 0, 0.5))

使用SizedBox而不是Container

一個 Container Widget非常靈活。例如,您可以自定義填充或邊框,而無需將其嵌套在另一個Widget中。但是,如果您只需要一個具有特定高度和寬度的框,最好使用 SizedBox Widget。它可以被設(shè)置為const,而 Container 則不行。

Row/Column, 中添加空格時,更傾向于使用 SizedBox 而不是 Container 。

@override
Widget build(BuildContext context) {
return Column(
  children: [ 
      Text(header),
      const SizedBox(height: 10), 
      Text(subheader), 
      Text(content)
    ]
  ); 
}

不要用 Clip

Clip是一項非常昂貴的操作,當(dāng)你的應(yīng)用程序變慢時應(yīng)該避免使用。如果Clip行為設(shè)置為 Clip.antiAliasWithSaveLayer ,它的代價會更高。嘗試找到其他不需要Clip的方法來實現(xiàn)你的目標(biāo)。例如,可以使用 borderRadius 屬性來創(chuàng)建帶有圓角邊框的矩形,而不是使用Clip。

Container(
  width: 100,
  height: 100,
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(50),
    image: DecorationImage(
      image: NetworkImage('https://example.com/image.jpg'),
      fit: BoxFit.cover,
    ),
  ),
)

使用Offstage

OffstageWidget允許您隱藏一個Widget,而不需要從Widget樹中移除它。這對于提高性能很有用,因為框架不需要重新構(gòu)建隱藏的Widget。

Offstage(
  offstage: !showWidget,
  child: MyWidget(),
)

在Flutter中, Offstage Widget用于在布局中隱藏子Widget,同時仍然是樹的一部分。它可以用于有條件地顯示或隱藏子Widget,而無需重新構(gòu)建整個樹。

Opacity Widget用于控制子Widget的透明度。它接受一個介于0.0和1.0之間的值,其中0.0表示完全透明,1.0表示完全不透明。然而,重要的是要注意它可能會影響性能,所以只在必要時使用。

Visibility Widget用于控制子Widget的可見性。它可以用于有條件地顯示或隱藏子Widget,而無需重新構(gòu)建整個樹。

所有三個Widget都用于控制子Widget的顯示,但它們的方式不同。

Offstage控制布局,Opacity控制透明度,Visibility控制可見性。

使用 addPostFrameCallback

在某些情況下,我們需要在幀渲染后執(zhí)行某些操作。不要嘗試使用任何延遲函數(shù),也不要創(chuàng)建自定義回調(diào)!我們可以使用 WidgetsBinding.instance.addPostFrameCallback 方法來實現(xiàn)。這個回調(diào)將在幀渲染后被調(diào)用,并通過避免不必要的重建來提高性能。

WidgetsBinding.instance.addPostFrameCallback((_) {
 // ...
});

使用 AutomaticKeepAliveClientMixin

當(dāng)使用 ListViewGridView 時,子部件可以被多次構(gòu)建。為了避免這種情況,我們可以使用 AutomaticKeepAliveClientMixin 來處理子部件。這將保持子部件的狀態(tài)并提高性能。

class MyChildWidget extends StatefulWidget {
  @override
  _MyChildWidgetState createState() => _MyChildWidgetState();
}

class _MyChildWidgetState extends State<MyChildWidget> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;
  
  @override
  Widget build(BuildContext context) {
    return Text("I am a child widget");
  }
}

在這個例子中, MyChildWidget 類使用 AutomaticKeepAliveClientMixin 混入,并且 wantKeepAlive 屬性被設(shè)置為 true 。這將保持 MyChildWidget 的狀態(tài),并防止它被多次重建,從而提高性能。

避免使用 MediaQuery.of(context).size

當(dāng)你在Flutter中使用MediaQuery.of(context).size時,F(xiàn)lutter會將你的小部件與MediaQuery的大小相關(guān)聯(lián)。這意味著每次調(diào)用MediaQuery.of(context).size時,F(xiàn)lutter會檢測MediaQuery的大小是否發(fā)生變化,從而可能導(dǎo)致不必要的重建(rebuilds)。

使用MediaQuery.sizeOf(context)來避免這些不必要的重建,從而提高應(yīng)用程序的響應(yīng)性。通過使用MediaQuery.sizeOf(context),你可以繞過與MediaQuery大小相關(guān)的重建過程,從而減少不必要的性能開銷。

類似的優(yōu)化方法也適用于其他MediaQuery方法。舉例來說,建議使用MediaQuery.platformBrightnessOf(context)而不是MediaQuery.of(context).platformBrightness,以避免不必要的重建,從而提高應(yīng)用的響應(yīng)性。

不要在調(diào)試模式下測量性能

一個用于性能和內(nèi)存測量的特殊模式,即Profile模式。您可以通過Android Studio或Visual Studio Code等IDE運行它,也可以通過執(zhí)行以下CLI命令來運行:

flutter run -profile

不要在模擬器中測量性能

多用真機性能調(diào)試

小結(jié)

優(yōu)化Flutter應(yīng)用的性能對于提供無縫的用戶體驗至關(guān)重要。通過實施這些提示,您可以進一步優(yōu)化Flutter應(yīng)用的性能。請記住,性能優(yōu)化是一個持續(xù)的過程,定期進行分析和測試是確保應(yīng)用程序保持高性能標(biāo)準(zhǔn)的關(guān)鍵。

感謝閱讀本文

如果有什么建議,請在評論中讓我知道。我很樂意改進。


? 貓哥
ducafecat.com

end

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