《Flutter全栈开发实战指南:从零到高级》- 25 -性能优化
2026/4/23 15:57:19 网站建设 项目流程

引言

当用户说某个App用起来很卡时,他们真正抱怨的是什么?
不是CPU使用率,不是内存占用,甚至不是帧率数字。用户感受到的是响应延迟界面跳帧操作不跟手。这就是为什么我们要做性能优化——不是为了让数字好看,而是为了让用户感知流畅。

根据Google的用户体验研究:

  • 100ms内响应:用户感觉瞬间完成;
  • 1秒内响应:用户感觉流畅自然;
  • 1-3秒响应:用户开始注意到延迟;
  • 3秒以上响应:用户感到不耐烦,面临卸载的可能;

今天,我们将从多个维度,深入理解Flutter性能优化的是什么为什么怎么做

一、优化构建性能

是什么导致了构建性能问题?

让我们先来看一张构建流程图:

setState调用
Widget树重建
Element树更新
RenderObject更新
布局计算
绘制执行
性能瓶颈来源
过度重建
复杂计算
深度嵌套
不当的Key使用

优化手段1:const构造函数 - 编译期优化

是什么:const构造函数创建的Widget在编译时确定,运行时不会重复构建。

为什么重要

  • 避免不必要的Widget实例创建
  • 减少垃圾回收压力
  • 提高热重载性能

怎么做

// 不推荐:每次build都创建新对象Widgetbuild(BuildContext context){returnContainer(padding:EdgeInsets.all(16),// 每次创建新EdgeInsetschild:Text('标题',style:TextStyle(fontSize:18)),// 每次创建新TextStyle);}// 优化写法:使用constWidgetbuild(BuildContext context){returnconstPadding(padding:EdgeInsets.all(16),// const EdgeInsetschild:Text('标题',style:TextStyle(fontSize:18),),);}// 推荐:提取常量classOptimizedWidgetextendsStatelessWidget{staticconst_padding=EdgeInsets.all(16);staticconst_textStyle=TextStyle(fontSize:18);@overrideWidgetbuild(BuildContext context){returnconstPadding(padding:_padding,child:Text('标题',style:_textStyle),);}}

注意点

  • const只能用于参数在编译时可确定的Widget
  • 带回调函数的Widget不能使用const
  • 列表中的item使用const效果最明显

优化手段2:Key的正确使用 - 控制Element复用

是什么:Key帮助Flutter识别Widget的身份,决定是否复用Element。

为什么重要

  • 错误的Key导致不必要的Element重建
  • 正确的Key保持状态在Widget移动时不被丢失

如何选择Key类型?

Key类型适用场景注意点
ValueKey基于值的唯一标识值变化时状态重置
ObjectKey基于对象的唯一标识适合对象列表
UniqueKey绝对唯一标识每次重建都不同
GlobalKey全局唯一标识谨慎使用,性能开销大
PageStorageKey保持滚动位置用于可滚动列表

怎么做

classKeyOptimizationDemoextendsStatefulWidget{@override_KeyOptimizationDemoStatecreateState()=>_KeyOptimizationDemoState();}class_KeyOptimizationDemoStateextendsState<KeyOptimizationDemo>{List<String>items=['A','B','C'];void_reverseList(){setState((){items=items.reversed.toList();});}@overrideWidgetbuild(BuildContext context){returnColumn(children:[ElevatedButton(onPressed:_reverseList,child:constText('反转列表'),),Expanded(child:ListView.builder(itemCount:items.length,itemBuilder:(context,index){finalitem=items[index];// 情况1:没有Key - 反转时状态会错乱// return StatefulListItem(text: item);// 情况2:使用index作为Key - 反转时状态错乱// return StatefulListItem(key: ValueKey(index), text: item);// 情况3:使用ValueKey - 反转时状态保持正确returnStatefulListItem(key:ValueKey(item),text:item);},),),],);}}classStatefulListItemextendsStatefulWidget{finalString text;constStatefulListItem({Key?key,requiredthis.text}):super(key:key);@override_StatefulListItemStatecreateState()=>_StatefulListItemState();}class_StatefulListItemStateextendsState<StatefulListItem>{int _counter=0;@overrideWidgetbuild(BuildContext context){returnListTile(title:Text('${widget.text} - 点击次数: $_counter'),onTap:()=>setState(()=>_counter++),);}}

优化手段3:RepaintBoundary - 隔离重绘区域

是什么:创建一个独立的绘制图层,避免不必要的重绘。

为什么重要

  • 频繁变化的组件不影响静态区域
  • 减少整体重绘范围
  • 提高渲染效率

适用场景

  • 频繁动画的组件
  • 独立滚动的列表
  • 视频播放器
  • 游戏画布

怎么做

classRepaintBoundaryDemoextendsStatelessWidget{@overrideWidgetbuild(BuildContext context){returnScaffold(body:Column(children:[// 静态区域,不需要重绘边界constStaticHeader(),// 频繁动画区域,需要隔离RepaintBoundary(child:AnimatedClock(),),// 静态区域constStaticContent(),// 独立滚动的列表Expanded(child:RepaintBoundary(child:ProductList(),),),],),);}}

注意点

  • 每个RepaintBoundary都有内存和性能开销
  • 不要过度使用,特别是在深度嵌套中
  • 优先隔离频繁变化的小区域

优化手段4:didUpdateWidget - 严格控制更新

是什么:StatefulWidget更新时的回调,可以精确控制哪些变化需要重建。

为什么重要

  • 避免不必要的setState调用
  • 可以只更新真正变化的部分
  • 减少重建范围

怎么做

classSmartWidgetextendsStatefulWidget{finalString title;finalList<int>data;constSmartWidget({Key?key,requiredthis.title,requiredthis.data}):super(key:key);@override_SmartWidgetStatecreateState()=>_SmartWidgetState();}class_SmartWidgetStateextendsState<SmartWidget>{late String _currentTitle;late List<int>_currentData;String?_computedResult;@overridevoidinitState(){super.initState();_currentTitle=widget.title;_currentData=widget.data;_computeResult();}@overridevoiddidUpdateWidget(SmartWidget oldWidget){super.didUpdateWidget(oldWidget);// 只有title变化时才重建UIif(widget.title!=_currentTitle){_currentTitle=widget.title;setState((){});}// data变化时重新计算,但不一定需要重建UIif(!_listEquals(widget.data,_currentData)){_currentData=widget.data;_computeResult();// 这里不调用setState,因为_computedResult可能被其他Widget使用}}void_computeResult(){_computedResult='计算结果: ${widget.data.length}';}@overrideWidgetbuild(BuildContext context){returnContainer(child:Column(children:[Text(_currentTitle),if(_computedResult!=null)Text(_computedResult!),],),);}}

二、优化内存

Flutter内存泄漏的根源?

内存泄漏
未释放的订阅
未取消的Timer
未dispose的Controller
循环引用
大对象未及时释放
泄漏影响
内存持续增长
OOM崩溃
应用被系统终止
用户体验差

优化手段1:管理资源生命周期

是什么:确保所有需要手动释放的资源都被正确释放。

为什么重要

  • Dart有垃圾回收,但某些资源需要手动管理
  • 未释放的资源会导致内存泄漏
  • 订阅和监听器可能持有Widget引用

需要管理的资源类型

  1. StreamSubscription
  2. Timer
  3. ScrollController/TextEditingController
  4. AnimationController
  5. FocusNode
  6. ImageStream

怎么做

// 典型的内存泄漏classLeakyWidgetextendsStatefulWidget{@override_LeakyWidgetStatecreateState()=>_LeakyWidgetState();}class_LeakyWidgetStateextendsState<LeakyWidget>{StreamSubscription?_subscription;Timer?_timer;ScrollController _controller=ScrollController();@overridevoidinitState(){super.initState();// 订阅Stream_subscription=Stream.periodic(Duration(seconds:1)).listen((_)=>print('tick'));// 启动Timer_timer=Timer.periodic(Duration(seconds:2),(_)=>print('timer'));// 添加监听器_controller.addListener(()=>print('scrolling'));}@overridevoiddispose(){// 忘记取消和释放// _subscription?.cancel();// _timer?.cancel();// _controller.dispose();super.dispose();}}// 正确做法:classSafeWidgetextendsStatefulWidget{@override_SafeWidgetStatecreateState()=>_SafeWidgetState();}class_SafeWidgetStateextendsState<SafeWidget>{latefinalStreamSubscription _subscription;latefinalTimer _timer;latefinalScrollController _controller;@overridevoidinitState(){super.initState();_controller=ScrollController();_controller.addListener(_onScroll);_subscription=Stream.periodic(Duration(seconds:1)).listen(_onTick);_timer=Timer.periodic(Duration(seconds:2),_onTimer);}void_onTick(_)=>print('tick');void_onTimer(_)=>print('timer');void_onScroll()=>print('scrolling');@overridevoiddispose(){// 按创建顺序的逆序释放_timer.cancel();_subscription.cancel();_controller.dispose();super.dispose();}}

优化手段2:大对象管理

是什么:针对图像、列表等大内存对象进行特殊管理。

为什么重要

  • 图像是移动应用内存的最大占用者
  • 不当的图像加载会导致OOM
  • 列表数据可能占用大量内存

优化策略

  1. 图像压缩和缓存
  2. 列表分页加载
  3. 对象池复用
  4. 懒加载和预加载平衡

怎么做

// 优化图像内存classImageMemoryOptimizer{// 1. 使用正确的图像尺寸staticWidgetbuildOptimizedImage(String url){returnImage.network(url,width:100,height:100,fit:BoxFit.cover,cacheWidth:200,cacheHeight:200,);}// 2. 使用缓存策略staticWidgetbuildCachedImage(String url){returnCachedNetworkImage(imageUrl:url,placeholder:(context,url)=>CircularProgressIndicator(),errorWidget:(context,url,error)=>Icon(Icons.error),width:100,height:100,fit:BoxFit.cover,memCacheWidth:200,memCacheHeight:200,maxWidthDiskCache:400,maxHeightDiskCache:400,);}// 3. 管理图片预加载staticfinalMap<String,ImageProvider>_preloadedImages={};staticFuture<void>preloadImage(String url)async{if(_preloadedImages.containsKey(url))return;finalcompleter=Completer<void>();finalimageProvider=NetworkImage(url);// 预加载到缓存finalstream=imageProvider.resolve(ImageConfiguration.empty);finallistener=ImageStreamListener((info,sync){_preloadedImages[url]=imageProvider;completer.complete();});stream.addListener(listener);awaitcompleter.future;stream.removeListener(listener);}}// 优化列表内存classListMemoryOptimizer{// 1. 分页加载staticFuture<List<Item>>loadItems(int page,int pageSize)async{finalstart=page*pageSize;finalend=start+pageSize;returnawait_fetchItems(start,end);}// 2. 重用列表项staticWidgetbuildReusableListItem(Item item){returnconstReusableListItem(item:item);}}

优化手段3:WeakReference使用

是什么:弱引用允许对象被垃圾回收,即使还有引用指向它。

为什么重要

  • 打破循环引用
  • 避免因监听器导致的内存泄漏
  • 允许缓存被自动清理

适用场景

  • 事件监听器
  • 缓存实现
  • 观察者模式

怎么做

// 使用弱引用的事件管理器import'dart:weak';classEventManager{finalList<WeakReference<EventListener>>_listeners=[];voidaddListener(EventListener listener){_listeners.add(WeakReference(listener));}voidnotify(String event){// 清理被回收的监听器_listeners.removeWhere((ref)=>ref.target==null);// 通知存活的监听器for(finalrefin_listeners){ref.target?.onEvent(event);}}}abstractclassEventListener{voidonEvent(String event);}// 具体使用classMyWidgetextendsStatefulWidget{@override_MyWidgetStatecreateState()=>_MyWidgetState();}class_MyWidgetStateextendsState<MyWidget>implementsEventListener{latefinalEventManager _eventManager;@overridevoidinitState(){super.initState();_eventManager=EventManager();_eventManager.addListener(this);}@overridevoidonEvent(String event){if(mounted){setState((){// 处理事件...});}}@overridevoiddispose(){// 不需要手动移除监听器super.dispose();}}

三、优化包体积

包体积为什么重要?

包体积过大的影响
下载量下降
存储空间占用
更新频率降低
低端设备体验差
体积构成分析
第一位:资源文件
第二位:Flutter引擎
第三位:Dart代码
第四位:三方库

优化手段1:优化资源文件

是什么:减少图片、字体等资源文件的大小。

为什么重要

  • 资源文件通常占用最大体积
  • 未使用的资源白白占用空间
  • 未优化的资源加载慢

优化手段

  1. 删除未使用资源
  2. 压缩图片格式
  3. 字体子集化
  4. 按需加载大资源

怎么做

# pubspec.yaml优化示例flutter:assets:# 不要导入整个目录# - assets/images/# 精确指定需要的文件-assets/images/icon.png-assets/images/logo.png-assets/images/splash.png# 使用WebP格式-assets/images/background.webpfonts:-family:NotoSansfonts:-asset:assets/fonts/NotoSans-Regular.ttf# 只包含需要的字体# - asset: assets/fonts/NotoSans-Bold.ttf# weight: 700
# 资源优化常用的终端命令:# 1. 查找大文件find.-name"*.png"-size +100k -execls-lh{}\;# 2. 转换为WebPcwebp -q80input.png -o output.webp# 3. 字体子集化pyftsubset font.ttf --text-file=chinese_chars.txt

优化手段2:代码混淆

是什么:通过混淆、压缩和优化减少代码体积。

为什么重要

  • 移除未使用的代码
  • 缩短标识符名称
  • 优化控制流

优化工具

  1. R8/ProGuard(Android)
  2. Dart编译优化
  3. Tree Shaking

怎么做

// android/app/build.gradle android { buildTypes { release { // 启用代码优化 minifyEnabled true shrinkResources true // 使用R8 useProguard true proguardFiles getDefaultProguardFile( 'proguard-android-optimize.txt' ), 'proguard-rules.pro' } } }
# proguard-rules.pro # 保留Flutter必要类 -keep class io.flutter.app.** { *; } -keep class io.flutter.plugin.** { *; } # 移除日志 -assumenosideeffects class android.util.Log { public static *** d(...); public static *** i(...); public static *** w(...); public static *** e(...); }

优化手段3:按需加载与延迟导入

是什么:将应用拆分为多个模块,按需加载。

为什么重要

  • 减少初始加载体积
  • 节省用户流量,提高启动速度

实现方式

  1. deferred import
  2. 动态特性模块
  3. 代码分割

怎么做

// 延迟导入import'package:heavy_library/heavy_library.dart'deferredasheavy;classLazyLoadWidgetextendsStatefulWidget{@override_LazyLoadWidgetStatecreateState()=>_LazyLoadWidgetState();}class_LazyLoadWidgetStateextendsState<LazyLoadWidget>{bool _isLoaded=false;Future<void>_loadLibrary()async{awaitheavy.loadLibrary();setState(()=>_isLoaded=true);}@overrideWidgetbuild(BuildContext context){if(!_isLoaded){returnElevatedButton(onPressed:_loadLibrary,child:constText('加载功能模块'),);}returnheavy.HeavyWidget();}}// 动态特性加载classFeatureManager{staticfinalMap<String,dynamic>_loadedFeatures={};staticFuture<dynamic>loadFeature(String featureName)async{if(_loadedFeatures.containsKey(featureName)){return_loadedFeatures[featureName];}switch(featureName){case'payment':finalpayment=awaitimport('package:app/payment.dart').deferredasdynamic;_loadedFeatures[featureName]=payment;returnpayment;case'analytics':// 按需加载分析模块finalanalytics=awaitimport('package:app/analytics.dart').deferredasdynamic;_loadedFeatures[featureName]=analytics;returnanalytics;}returnnull;}}

优化手段4:优化应用Bundle

是什么:利用平台特性进行智能分发。

为什么重要

  • Android App Bundle减少下载体积
  • iOS App Thinning按设备分发
  • 支持动态功能模块

实现方式

  1. Android App Bundle
  2. iOS App Thinning
  3. 动态功能交付

怎么做

// 启用Android App Bundle android { bundle { language { enableSplit = true // 按语言拆分 } density { enableSplit = true // 按屏幕密度拆分 } abi { enableSplit = true // 按CPU架构拆分 } } }
# 构建命令# 1. 构建Android App Bundleflutter build appbundle# 2. 分析Bundleflutter build appbundle --target-platform android-arm64 --analyze-size# 3. 测试Bundlebundletool build-apks --bundle=app.aab --output=app.apks bundletool install-apks --apks=app.apks

四、优化渲染性能

什么导致了渲染卡顿?

渲染卡顿原因
复杂布局计算
频繁绘制操作
图层合成开销
GPU负载过高
嵌套过深
约束传递频繁
过度使用阴影
透明度处理不当
频繁裁剪操作
RepaintBoundary过多
图层混合模式复杂

优化手段1:优化布局性能

是什么:减少布局计算的时间和复杂度。

为什么重要

  • 布局是渲染管线中最耗时的阶段之一
  • 复杂的布局导致界面卡顿
  • 不当的约束传递引发连锁重排

优化手段

  1. 减少Widget嵌套深度
  2. 使用合适的布局Widget
  3. 避免过度使用Flexible/Expanded
  4. 使用CustomSingleChildLayout/CustomMultiChildLayout

怎么做

// 不推荐:深度嵌套WidgetbuildBadLayout(){returnContainer(child:Column(children:[Container(child:Row(children:[Container(child:Text('A')),Container(child:Text('B')),Container(child:Text('C')),],),),// 更多嵌套...],),);}// 优化的布局WidgetbuildGoodLayout(){returnColumn(children:[Row(children:const[Text('A'),SizedBox(width:8),Text('B'),SizedBox(width:8),Text('C'),],),// 使用间隔Widget替代多余的ContainerconstSizedBox(height:16),],);}// 使用CustomMultiChildLayout优化复杂布局classOptimizedLayoutextendsStatelessWidget{@overrideWidgetbuild(BuildContext context){returnCustomMultiChildLayout(delegate:_LayoutDelegate(),children:[LayoutId(id:_LayoutItem.header,child:constHeaderWidget(),),LayoutId(id:_LayoutItem.content,child:constContentWidget(),),LayoutId(id:_LayoutItem.footer,child:constFooterWidget(),),],);}}class_LayoutDelegateextendsMultiChildLayoutDelegate{@overridevoidperformLayout(Size size){// 一次性计算所有子项的位置和大小finalheaderSize=layoutChild(_LayoutItem.header,BoxConstraints.loose(size),);finalfooterSize=layoutChild(_LayoutItem.footer,BoxConstraints.loose(size),);finalcontentConstraints=BoxConstraints(maxWidth:size.width,maxHeight:size.height-headerSize.height-footerSize.height,);layoutChild(_LayoutItem.content,contentConstraints);// 定位子项positionChild(_LayoutItem.header,Offset.zero);positionChild(_LayoutItem.content,Offset(0,headerSize.height));positionChild(_LayoutItem.footer,Offset(0,size.height-footerSize.height));}@overrideboolshouldRelayout(covariantMultiChildLayoutDelegate oldDelegate){returnfalse;}}enum_LayoutItem{header,content,footer}

优化手段2:优化绘制性能

是什么:减少复杂绘制操作频率。

为什么重要

  • 复杂的绘制操作消耗GPU资源
  • 过度绘制浪费渲染时间
  • 不当的效果使用导致性能变差

优化策略

  1. 避免过度使用阴影
  2. 谨慎使用透明度
  3. 减少裁剪操作
  4. 使用缓存图片

怎么做

// 优化绘制classPaintOptimization{// 1. 优化阴影staticBoxDecorationoptimizedShadow(){returnBoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(8),// 过度使用阴影// boxShadow: [// BoxShadow(color: Colors.black12, blurRadius: 16),// BoxShadow(color: Colors.black12, blurRadius: 8),// BoxShadow(color: Colors.black12, blurRadius: 4),// ],// 优化后boxShadow:const[BoxShadow(color:Colors.black12,blurRadius:4,spreadRadius:0,offset:Offset(0,2),),],);}// 2. 优化透明度staticWidgetoptimizedOpacity(){// 使用Opacity Widget// return Opacity(// opacity: 0.5,// child: Container(color: Colors.blue),// );// 使用颜色透明度returnContainer(color:Colors.blue.withOpacity(0.5),);// 对于静态半透明,使用ColorFiltered// return ColorFiltered(// colorFilter: ColorFilter.mode(// Colors.white.withOpacity(0.5),// BlendMode.modulate,// ),// child: Container(color: Colors.blue),// );}// 3. 优化裁剪staticWidgetoptimizedClip(){// 复杂裁剪// return ClipPath(// clipper: ComplexClipper(),// child: Container(color: Colors.blue),// );// 简单裁剪returnClipRRect(borderRadius:BorderRadius.circular(8),child:Container(color:Colors.blue),);// 使用装饰而不是裁剪// return Container(// decoration: BoxDecoration(// color: Colors.blue,// borderRadius: BorderRadius.circular(8),// ),// );}}

优化手段3:优化列表性能

是什么:针对ListView、GridView等可滚动组件进行优化。

为什么重要

  • 几乎所有的应用都会用到列表组件,他的性能好坏直接影响用户体验
  • 不当的列表实现导致滚动卡顿

优化策略

  1. 使用正确的ListView构造函数
  2. 设置合理的缓存范围
  3. 优化列表项构建
  4. 使用Sliver系列组件

怎么做

classListOptimization{// 1. 选择合适的ListView构造函数staticWidgetbuildOptimizedList(List<Item>items){returnListView.builder(itemCount:items.length,// 优化参数addAutomaticKeepAlives:false,// 手动控制状态保持addRepaintBoundaries:true,// 缓存范围,预渲染区域cacheExtent:1000,// 默认250,增大可减少滚动卡顿// 如果item高度固定,使用itemExtent提高性能// itemExtent: 100,// 或者使用prototypeItem// prototypeItem: const ListTile(title: Text('原型')),itemBuilder:(context,index){return_buildOptimizedItem(items[index]);},);}// 2. 优化列表项构建staticWidget_buildOptimizedItem(Item item){returnRepaintBoundary(child:KeepAlive(keepAlive:_shouldKeepAlive(item),child:constOptimizedListItem(item:item),),);}staticbool_shouldKeepAlive(Item item){returnitem.isImportant;}// 3. 使用CustomScrollView和SliversstaticWidgetbuildCustomScrollView(){returnCustomScrollView(slivers:[// 固定AppBarconstSliverAppBar(pinned:true,expandedHeight:200),// 固定HeaderconstSliverToBoxAdapter(child:Padding(padding:EdgeInsets.all(16),child:Text('商品列表'),),),// 使用SliverFixedExtentListSliverFixedExtentList(itemExtent:100,delegate:SliverChildBuilderDelegate((context,index)=>_buildOptimizedItem(_getItem(index)),childCount:1000,),),],);}}

优化手段4:优化动画性能

是什么:优化动画的流畅度和性能。

为什么重要

  • 动画流畅度是用户体验的最直观感受,复杂的动画会消耗大量资源,也会导致卡顿

优化策略

  1. 使用AnimatedWidget/AnimatedBuilder
  2. 避免在动画中调用setState
  3. 使用TweenAnimationBuilder
  4. 合理使用物理动画

怎么做

classAnimationOptimization{// 1. 使用AnimatedBuilder避免不必要的重建staticWidgetbuildOptimizedAnimation(){returnAnimatedBuilder(animation:_animationController,builder:(context,child){returnTransform.rotate(angle:_animationController.value*2*pi,child:child,);},child:constIcon(Icons.refresh),);}// 2. 使用TweenAnimationBuilder动画staticWidgetbuildTweenAnimation(){returnTweenAnimationBuilder<double>(tween:Tween(begin:0,end:1),duration:Duration(seconds:1),builder:(context,value,child){returnOpacity(opacity:value,child:Transform.scale(scale:value,child:child),);},child:constText('淡入放大动画'),);}// 3. 优化物理动画staticWidgetbuildPhysicsAnimation(){returnDraggable(feedback:Material(child:Container(width:100,height:100,color:Colors.blue.withOpacity(0.5),),),childWhenDragging:Container(),child:Container(width:100,height:100,color:Colors.blue,),feedbackOffset:Offset.zero,);}}

五、性能监控

工具链

性能监控工具链
开发阶段
测试阶段
生产阶段
Flutter DevTools
Dart Observatory
性能覆盖层
自动化测试
性能测试
内存泄漏测试
APM集成
崩溃监控
用户行为分析

工具使用

// 1. 性能覆盖层voidenablePerformanceOverlay(){runApp(MaterialApp(home:constMyApp(),showPerformanceOverlay:true,// 其他调试选项checkerboardRasterCacheImages:true,checkerboardOffscreenLayers:true,showSemanticsDebugger:true,),);}// 2. 自定义性能监控classPerformanceMonitorextendsStatefulWidget{finalWidget child;constPerformanceMonitor({Key?key,requiredthis.child}):super(key:key);@override_PerformanceMonitorStatecreateState()=>_PerformanceMonitorState();}class_PerformanceMonitorStateextendsState<PerformanceMonitor>withWidgetsBindingObserver{finalList<double>_frameTimes=[];double _fps=0;int _droppedFrames=0;@overridevoidinitState(){super.initState();WidgetsBinding.instance!.addObserver(this);}@overridevoiddidChangeMetrics(){// 监控帧率SchedulerBinding.instance!.addPostFrameCallback((timeStamp){_recordFrameTime(timeStamp);});}void_recordFrameTime(Duration timeStamp){finalnow=timeStamp.inMicroseconds/1000;_frameTimes.add(now);// 保留最近1秒的数据finaloneSecondAgo=now-1000;_frameTimes.removeWhere((time)=>time<oneSecondAgo);// 计算FPSif(_frameTimes.length>=2){finalfps=(_frameTimes.length-1)*1000/(_frameTimes.last-_frameTimes.first);// 计算掉帧finalframeInterval=_frameTimes.last-_frameTimes[_frameTimes.length-2];if(frameInterval>33.34){_droppedFrames++;}setState((){_fps=fps;});}}@overrideWidgetbuild(BuildContext context){returnStack(children:[widget.child,if(kDebugMode)// 只在调试模式显示Positioned(top:40,right:10,child:Container(padding:constEdgeInsets.all(8),color:_getFPSColor(),child:Text('FPS: ${_fps.toStringAsFixed(1)}\n''掉帧: $_droppedFrames',style:constTextStyle(color:Colors.white),),),),],);}Color_getFPSColor(){if(_fps>=55)returnColors.green;if(_fps>=30)returnColors.orange;returnColors.red;}}

六、性能优化优先级

基于影响范围和实施难度等多重因素,建议性能优化的优先级顺序如下:

优先级优化方向影响范围实施难度推荐工具
P0修复内存泄漏整个应用DevTools, LeakCanary
P0优化启动时间首次体验Flutter Performance
P1优化列表滚动核心功能Performance Overlay
P1优化包体积下载率Analyze Size
P2优化动画流畅度用户体验Flutter Inspector
P2优化构建性能开发体验Dart Analyzer
P3优化渲染管线特定场景Custom RenderObject

总结

至此性能优化的知识点就全部介绍完了,其核心:

  • 需要从架构设计阶段开始考虑
  • 不同的应用场景需要不同的优化策略
  • 数据驱动
  • 所有优化都要服务于用户体验
  • 性能优化是持续的过程

过早优化是万恶之源,但不优化是用户体验的灾难。优化在于正确的时间,用正确的方法,优化正确的地方。


如果觉得这篇文章有帮助,别忘了一键三连支持一下!有任何问题或建议,欢迎在评论区交流讨论!
转载请注明出处~~~

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询