
視頻
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

之前錄過一個 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
);
}
}
列表中的重用:在ListView或GridView等可滾動列表中,使用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的主要作用包括:
減少重繪范圍:通過將子部件包裹在
RepaintBoundary中,可以將其視為一個整體,僅在該部件內(nèi)部發(fā)生重繪時才重新繪制,而不會影響到其他部分。性能優(yōu)化:避免不必要的重繪操作,可以提高應(yīng)用程序的性能,特別是在具有復(fù)雜界面或動態(tài)內(nèi)容的情況下。
避免全局重繪:在某些情況下,只需要更新特定部分的UI,而不是整個界面。通過使用
RepaintBoundary,可以限制重繪的范圍,避免全局重繪。邊界控制:可以通過
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)使用 ListView 或 GridView 時,子部件可以被多次構(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