引言
当用户说某个App用起来很卡时,他们真正抱怨的是什么?
不是CPU使用率,不是内存占用,甚至不是帧率数字。用户感受到的是响应延迟、界面跳帧和操作不跟手。这就是为什么我们要做性能优化——不是为了让数字好看,而是为了让用户感知流畅。
根据Google的用户体验研究:
- 100ms内响应:用户感觉瞬间完成;
- 1秒内响应:用户感觉流畅自然;
- 1-3秒响应:用户开始注意到延迟;
- 3秒以上响应:用户感到不耐烦,面临卸载的可能;
今天,我们将从多个维度,深入理解Flutter性能优化的是什么、为什么和怎么做。
一、优化构建性能
是什么导致了构建性能问题?
让我们先来看一张构建流程图:
优化手段1:const构造函数 - 编译期优化
是什么:const构造函数创建的Widget在编译时确定,运行时不会重复构建。
为什么重要:
- 避免不必要的Widget实例创建
- 减少垃圾回收压力
- 提高热重载性能
怎么做:
// 不推荐:每次build都创建新对象
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16), // 每次创建新EdgeInsets
child: Text('标题', style: TextStyle(fontSize: 18)), // 每次创建新TextStyle
);
}
// 优化写法:使用const
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.all(16), // const EdgeInsets
child: Text(
'标题',
style: TextStyle(fontSize: 18),
),
);
}
// 推荐:提取常量
class OptimizedWidget extends StatelessWidget {
static const _padding = EdgeInsets.all(16);
static const _textStyle = TextStyle(fontSize: 18);
Widget build(BuildContext context) {
return const Padding(
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 | 保持滚动位置 | 用于可滚动列表 |
怎么做:
class KeyOptimizationDemo extends StatefulWidget {
_KeyOptimizationDemoState createState() => _KeyOptimizationDemoState();
}
class _KeyOptimizationDemoState extends State<KeyOptimizationDemo> {
List<String> items = ['A', 'B', 'C'];
void _reverseList() {
setState(() {
items = items.reversed.toList();
});
}
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: _reverseList,
child: const Text('反转列表'),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
// 情况1:没有Key - 反转时状态会错乱
// return StatefulListItem(text: item);
// 情况2:使用index作为Key - 反转时状态错乱
// return StatefulListItem(key: ValueKey(index), text: item);
// 情况3:使用ValueKey - 反转时状态保持正确
return StatefulListItem(key: ValueKey(item), text: item);
},
),
),
],
);
}
}
class StatefulListItem extends StatefulWidget {
final String text;
const StatefulListItem({Key? key, required this.text}) : super(key: key);
_StatefulListItemState createState() => _StatefulListItemState();
}
class _StatefulListItemState extends State<StatefulListItem> {
int _counter = 0;
Widget build(BuildContext context) {
return ListTile(
title: Text('${widget.text} - 点击次数: $_counter'),
onTap: () => setState(() => _counter++),
);
}
}
优化手段3:RepaintBoundary - 隔离重绘区域
是什么:创建一个独立的绘制图层,避免不必要的重绘。
为什么重要:
- 频繁变化的组件不影响静态区域
- 减少整体重绘范围
- 提高渲染效率
适用场景:
- 频繁动画的组件
- 独立滚动的列表
- 视频播放器
- 游戏画布
怎么做:
class RepaintBoundaryDemo extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
// 静态区域,不需要重绘边界
const StaticHeader(),
// 频繁动画区域,需要隔离
RepaintBoundary(
child: AnimatedClock(),
),
// 静态区域
const StaticContent(),
// 独立滚动的列表
Expanded(
child: RepaintBoundary(
child: ProductList(),
),
),
],
),
);
}
}
注意点:
- 每个RepaintBoundary都有内存和性能开销
- 不要过度使用,特别是在深度嵌套中
- 优先隔离频繁变化的小区域
优化手段4:didUpdateWidget - 严格控制更新
是什么:StatefulWidget更新时的回调,可以精确控制哪些变化需要重建。
为什么重要:
- 避免不必要的setState调用
- 可以只更新真正变化的部分
- 减少重建范围
怎么做:
class SmartWidget extends StatefulWidget {
final String title;
final List<int> data;
const SmartWidget({Key? key, required this.title, required this.data})
: super(key: key);
_SmartWidgetState createState() => _SmartWidgetState();
}
class _SmartWidgetState extends State<SmartWidget> {
late String _currentTitle;
late List<int> _currentData;
String? _computedResult;
void initState() {
super.initState();
_currentTitle = widget.title;
_currentData = widget.data;
_computeResult();
}
void didUpdateWidget(SmartWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 只有title变化时才重建UI
if (widget.title != _currentTitle) {
_currentTitle = widget.title;
setState(() {});
}
// data变化时重新计算,但不一定需要重建UI
if (!_listEquals(widget.data, _currentData)) {
_currentData = widget.data;
_computeResult();
// 这里不调用setState,因为_computedResult可能被其他Widget使用
}
}
void _computeResult() {
_computedResult = '计算结果: ${widget.data.length}';
}
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text(_currentTitle),
if (_computedResult != null) Text(_computedResult!),
],
),
);
}
}
二、优化内存
Flutter内存泄漏的根源?
优化手段1:管理资源生命周期
是什么:确保所有需要手动释放的资源都被正确释放。
为什么重要:
- Dart有垃圾回收,但某些资源需要手动管理
- 未释放的资源会导致内存泄漏
- 订阅和监听器可能持有Widget引用
需要管理的资源类型:
- StreamSubscription
- Timer
- ScrollController/TextEditingController
- AnimationController
- FocusNode
- ImageStream
怎么做:
// 典型的内存泄漏
class LeakyWidget extends StatefulWidget {
_LeakyWidgetState createState() => _LeakyWidgetState();
}
class _LeakyWidgetState extends State<LeakyWidget> {
StreamSubscription? _subscription;
Timer? _timer;
ScrollController _controller = ScrollController();
void initState() {
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'));
}
void dispose() {
// 忘记取消和释放
// _subscription?.cancel();
// _timer?.cancel();
// _controller.dispose();
super.dispose();
}
}
// 正确做法:
class SafeWidget extends StatefulWidget {
_SafeWidgetState createState() => _SafeWidgetState();
}
class _SafeWidgetState extends State<SafeWidget> {
late final StreamSubscription _subscription;
late final Timer _timer;
late final ScrollController _controller;
void initState() {
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');
void dispose() {
// 按创建顺序的逆序释放
_timer.cancel();
_subscription.cancel();
_controller.dispose();
super.dispose();
}
}
优化手段2:大对象管理
是什么:针对图像、列表等大内存对象进行特殊管理。
为什么重要:
- 图像是移动应用内存的最大占用者
- 不当的图像加载会导致OOM
- 列表数据可能占用大量内存
优化策略:
- 图像压缩和缓存
- 列表分页加载
- 对象池复用
- 懒加载和预加载平衡
怎么做:
// 优化图像内存
class ImageMemoryOptimizer {
// 1. 使用正确的图像尺寸
static Widget buildOptimizedImage(String url) {
return Image.network(
url,
width: 100,
height: 100,
fit: BoxFit.cover,
cacheWidth: 200,
cacheHeight: 200,
);
}
// 2. 使用缓存策略
static Widget buildCachedImage(String url) {
return CachedNetworkImage(
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. 管理图片预加载
static final Map<String, ImageProvider> _preloadedImages = {};
static Future<void> preloadImage(String url) async {
if (_preloadedImages.containsKey(url)) return;
final completer = Completer<void>();
final imageProvider = NetworkImage(url);
// 预加载到缓存
final stream = imageProvider.resolve(ImageConfiguration.empty);
final listener = ImageStreamListener((info, sync) {
_preloadedImages[url] = imageProvider;
completer.complete();
});
stream.addListener(listener);
await completer.future;
stream.removeListener(listener);
}
}
// 优化列表内存
class ListMemoryOptimizer {
// 1. 分页加载
static Future<List<Item>> loadItems(int page, int pageSize) async {
final start = page * pageSize;
final end = start + pageSize;
return await _fetchItems(start, end);
}
// 2. 重用列表项
static Widget buildReusableListItem(Item item) {
return const ReusableListItem(item: item);
}
}
优化手段3:WeakReference使用
是什么:弱引用允许对象被垃圾回收,即使还有引用指向它。
为什么重要:
- 打破循环引用
- 避免因监听器导致的内存泄漏
- 允许缓存被自动清理
适用场景:
- 事件监听器
- 缓存实现
- 观察者模式
怎么做:
// 使用弱引用的事件管理器
import 'dart:weak';
class EventManager {
final List<WeakReference<EventListener>> _listeners = [];
void addListener(EventListener listener) {
_listeners.add(WeakReference(listener));
}
void notify(String event) {
// 清理被回收的监听器
_listeners.removeWhere((ref) => ref.target == null);
// 通知存活的监听器
for (final ref in _listeners) {
ref.target?.onEvent(event);
}
}
}
abstract class EventListener {
void onEvent(String event);
}
// 具体使用
class MyWidget extends StatefulWidget {
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> implements EventListener {
late final EventManager _eventManager;
void initState() {
super.initState();
_eventManager = EventManager();
_eventManager.addListener(this);
}
void onEvent(String event) {
if (mounted) {
setState(() {
// 处理事件...
});
}
}
void dispose() {
// 不需要手动移除监听器
super.dispose();
}
}
三、优化包体积
包体积为什么重要?
优化手段1:优化资源文件
是什么:减少图片、字体等资源文件的大小。
为什么重要:
- 资源文件通常占用最大体积
- 未使用的资源白白占用空间
- 未优化的资源加载慢
优化手段:
- 删除未使用资源
- 压缩图片格式
- 字体子集化
- 按需加载大资源
怎么做:
# pubspec.yaml优化示例
flutter:
assets:
# 不要导入整个目录
# - assets/images/
# 精确指定需要的文件
- assets/images/icon.png
- assets/images/logo.png
- assets/images/splash.png
# 使用WebP格式
- assets/images/background.webp
fonts:
- family: NotoSans
fonts:
- asset: assets/fonts/NotoSans-Regular.ttf
# 只包含需要的字体
# - asset: assets/fonts/NotoSans-Bold.ttf
# weight: 700
# 资源优化常用的终端命令:
# 1. 查找大文件
find . -name "*.png" -size +100k -exec ls -lh {} \;
# 2. 转换为WebP
cwebp -q 80 input.png -o output.webp
# 3. 字体子集化
pyftsubset font.ttf --text-file=chinese_chars.txt
优化手段2:代码混淆
是什么:通过混淆、压缩和优化减少代码体积。
为什么重要:
- 移除未使用的代码
- 缩短标识符名称
- 优化控制流
优化工具:
- R8/ProGuard(Android)
- Dart编译优化
- 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:按需加载与延迟导入
是什么:将应用拆分为多个模块,按需加载。
为什么重要:
- 减少初始加载体积
- 节省用户流量,提高启动速度
实现方式:
- deferred import
- 动态特性模块
- 代码分割
怎么做:
// 延迟导入
import 'package:heavy_library/heavy_library.dart' deferred as heavy;
class LazyLoadWidget extends StatefulWidget {
_LazyLoadWidgetState createState() => _LazyLoadWidgetState();
}
class _LazyLoadWidgetState extends State<LazyLoadWidget> {
bool _isLoaded = false;
Future<void> _loadLibrary() async {
await heavy.loadLibrary();
setState(() => _isLoaded = true);
}
Widget build(BuildContext context) {
if (!_isLoaded) {
return ElevatedButton(
onPressed: _loadLibrary,
child: const Text('加载功能模块'),
);
}
return heavy.HeavyWidget();
}
}
// 动态特性加载
class FeatureManager {
static final Map<String, dynamic> _loadedFeatures = {};
static Future<dynamic> loadFeature(String featureName) async {
if (_loadedFeatures.containsKey(featureName)) {
return _loadedFeatures[featureName];
}
switch (featureName) {
case 'payment':
final payment = await import('package:app/payment.dart')
.deferred as dynamic;
_loadedFeatures[featureName] = payment;
return payment;
case 'analytics':
// 按需加载分析模块
final analytics = await import('package:app/analytics.dart')
.deferred as dynamic;
_loadedFeatures[featureName] = analytics;
return analytics;
}
return null;
}
}
优化手段4:优化应用Bundle
是什么:利用平台特性进行智能分发。
为什么重要:
- Android App Bundle减少下载体积
- iOS App Thinning按设备分发
- 支持动态功能模块
实现方式:
- Android App Bundle
- iOS App Thinning
- 动态功能交付
怎么做:
// 启用Android App Bundle
android {
bundle {
language {
enableSplit = true // 按语言拆分
}
density {
enableSplit = true // 按屏幕密度拆分
}
abi {
enableSplit = true // 按CPU架构拆分
}
}
}
# 构建命令
# 1. 构建Android App Bundle
flutter build appbundle
# 2. 分析Bundle
flutter build appbundle --target-platform android-arm64 --analyze-size
# 3. 测试Bundle
bundletool build-apks --bundle=app.aab --output=app.apks
bundletool install-apks --apks=app.apks
四、优化渲染性能
什么导致了渲染卡顿?
优化手段1:优化布局性能
是什么:减少布局计算的时间和复杂度。
为什么重要:
- 布局是渲染管线中最耗时的阶段之一
- 复杂的布局导致界面卡顿
- 不当的约束传递引发连锁重排
优化手段:
- 减少Widget嵌套深度
- 使用合适的布局Widget
- 避免过度使用Flexible/Expanded
- 使用CustomSingleChildLayout/CustomMultiChildLayout
怎么做:
// 不推荐:深度嵌套
Widget buildBadLayout() {
return Container(
child: Column(
children: [
Container(
child: Row(
children: [
Container(child: Text('A')),
Container(child: Text('B')),
Container(child: Text('C')),
],
),
),
// 更多嵌套...
],
),
);
}
// 优化的布局
Widget buildGoodLayout() {
return Column(
children: [
Row(
children: const [
Text('A'),
SizedBox(width: 8),
Text('B'),
SizedBox(width: 8),
Text('C'),
],
),
// 使用间隔Widget替代多余的Container
const SizedBox(height: 16),
],
);
}
// 使用CustomMultiChildLayout优化复杂布局
class OptimizedLayout extends StatelessWidget {
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: _LayoutDelegate(),
children: [
LayoutId(
id: _LayoutItem.header,
child: const HeaderWidget(),
),
LayoutId(
id: _LayoutItem.content,
child: const ContentWidget(),
),
LayoutId(
id: _LayoutItem.footer,
child: const FooterWidget(),
),
],
);
}
}
class _LayoutDelegate extends MultiChildLayoutDelegate {
void performLayout(Size size) {
// 一次性计算所有子项的位置和大小
final headerSize = layoutChild(
_LayoutItem.header,
BoxConstraints.loose(size),
);
final footerSize = layoutChild(
_LayoutItem.footer,
BoxConstraints.loose(size),
);
final contentConstraints = 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));
}
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
return false;
}
}
enum _LayoutItem { header, content, footer }
优化手段2:优化绘制性能
是什么:减少复杂绘制操作频率。
为什么重要:
- 复杂的绘制操作消耗GPU资源
- 过度绘制浪费渲染时间
- 不当的效果使用导致性能变差
优化策略:
- 避免过度使用阴影
- 谨慎使用透明度
- 减少裁剪操作
- 使用缓存图片
怎么做:
// 优化绘制
class PaintOptimization {
// 1. 优化阴影
static BoxDecoration optimizedShadow() {
return BoxDecoration(
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. 优化透明度
static Widget optimizedOpacity() {
// 使用Opacity Widget
// return Opacity(
// opacity: 0.5,
// child: Container(color: Colors.blue),
// );
// 使用颜色透明度
return Container(
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. 优化裁剪
static Widget optimizedClip() {
// 复杂裁剪
// return ClipPath(
// clipper: ComplexClipper(),
// child: Container(color: Colors.blue),
// );
// 简单裁剪
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(color: Colors.blue),
);
// 使用装饰而不是裁剪
// return Container(
// decoration: BoxDecoration(
// color: Colors.blue,
// borderRadius: BorderRadius.circular(8),
// ),
// );
}
}
优化手段3:优化列表性能
是什么:针对ListView、GridView等可滚动组件进行优化。
为什么重要:
- 几乎所有的应用都会用到列表组件,他的性能好坏直接影响用户体验
- 不当的列表实现导致滚动卡顿
优化策略:
- 使用正确的ListView构造函数
- 设置合理的缓存范围
- 优化列表项构建
- 使用Sliver系列组件
怎么做:
class ListOptimization {
// 1. 选择合适的ListView构造函数
static Widget buildOptimizedList(List<Item> items) {
return ListView.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. 优化列表项构建
static Widget _buildOptimizedItem(Item item) {
return RepaintBoundary(
child: KeepAlive(
keepAlive: _shouldKeepAlive(item),
child: const OptimizedListItem(item: item),
),
);
}
static bool _shouldKeepAlive(Item item) {
return item.isImportant;
}
// 3. 使用CustomScrollView和Slivers
static Widget buildCustomScrollView() {
return CustomScrollView(
slivers: [
// 固定AppBar
const SliverAppBar(pinned: true, expandedHeight: 200),
// 固定Header
const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(16),
child: Text('商品列表'),
),
),
// 使用SliverFixedExtentList
SliverFixedExtentList(
itemExtent: 100,
delegate: SliverChildBuilderDelegate(
(context, index) => _buildOptimizedItem(_getItem(index)),
childCount: 1000,
),
),
],
);
}
}
优化手段4:优化动画性能
是什么:优化动画的流畅度和性能。
为什么重要:
- 动画流畅度是用户体验的最直观感受,复杂的动画会消耗大量资源,也会导致卡顿
优化策略:
- 使用AnimatedWidget/AnimatedBuilder
- 避免在动画中调用setState
- 使用TweenAnimationBuilder
- 合理使用物理动画
怎么做:
class AnimationOptimization {
// 1. 使用AnimatedBuilder避免不必要的重建
static Widget buildOptimizedAnimation() {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.rotate(
angle: _animationController.value * 2 * pi,
child: child,
);
},
child: const Icon(Icons.refresh),
);
}
// 2. 使用TweenAnimationBuilder动画
static Widget buildTweenAnimation() {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1),
duration: Duration(seconds: 1),
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.scale(scale: value, child: child),
);
},
child: const Text('淡入放大动画'),
);
}
// 3. 优化物理动画
static Widget buildPhysicsAnimation() {
return Draggable(
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,
);
}
}
五、性能监控
工具链
工具使用
// 1. 性能覆盖层
void enablePerformanceOverlay() {
runApp(
MaterialApp(
home: const MyApp(),
showPerformanceOverlay: true,
// 其他调试选项
checkerboardRasterCacheImages: true,
checkerboardOffscreenLayers: true,
showSemanticsDebugger: true,
),
);
}
// 2. 自定义性能监控
class PerformanceMonitor extends StatefulWidget {
final Widget child;
const PerformanceMonitor({Key? key, required this.child}) : super(key: key);
_PerformanceMonitorState createState() => _PerformanceMonitorState();
}
class _PerformanceMonitorState extends State<PerformanceMonitor>
with WidgetsBindingObserver {
final List<double> _frameTimes = [];
double _fps = 0;
int _droppedFrames = 0;
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
void didChangeMetrics() {
// 监控帧率
SchedulerBinding.instance!.addPostFrameCallback((timeStamp) {
_recordFrameTime(timeStamp);
});
}
void _recordFrameTime(Duration timeStamp) {
final now = timeStamp.inMicroseconds / 1000;
_frameTimes.add(now);
// 保留最近1秒的数据
final oneSecondAgo = now - 1000;
_frameTimes.removeWhere((time) => time < oneSecondAgo);
// 计算FPS
if (_frameTimes.length >= 2) {
final fps = (_frameTimes.length - 1) * 1000 /
(_frameTimes.last - _frameTimes.first);
// 计算掉帧
final frameInterval = _frameTimes.last -
_frameTimes[_frameTimes.length - 2];
if (frameInterval > 33.34) {
_droppedFrames++;
}
setState(() {
_fps = fps;
});
}
}
Widget build(BuildContext context) {
return Stack(
children: [
widget.child,
if (kDebugMode) // 只在调试模式显示
Positioned(
top: 40,
right: 10,
child: Container(
padding: const EdgeInsets.all(8),
color: _getFPSColor(),
child: Text(
'FPS: ${_fps.toStringAsFixed(1)}\n'
'掉帧: $_droppedFrames',
style: const TextStyle(color: Colors.white),
),
),
),
],
);
}
Color _getFPSColor() {
if (_fps >= 55) return Colors.green;
if (_fps >= 30) return Colors.orange;
return Colors.red;
}
}
六、性能优化优先级
基于影响范围和实施难度等多重因素,建议性能优化的优先级顺序如下:
| 优先级 | 优化方向 | 影响范围 | 实施难度 | 推荐工具 |
|---|---|---|---|---|
| P0 | 修复内存泄漏 | 整个应用 | 低 | DevTools, LeakCanary |
| P0 | 优化启动时间 | 首次体验 | 中 | Flutter Performance |
| P1 | 优化列表滚动 | 核心功能 | 中 | Performance Overlay |
| P1 | 优化包体积 | 下载率 | 中 | Analyze Size |
| P2 | 优化动画流畅度 | 用户体验 | 高 | Flutter Inspector |
| P2 | 优化构建性能 | 开发体验 | 低 | Dart Analyzer |
| P3 | 优化渲染管线 | 特定场景 | 高 | Custom RenderObject |
总结
至此性能优化的知识点就全部介绍完了,其核心:
- 需要从架构设计阶段开始考虑
- 不同的应用场景需要不同的优化策略
- 数据驱动
- 所有优化都要服务于用户体验
- 性能优化是持续的过程
过早优化是万恶之源,但不优化是用户体验的灾难。 优化在于正确的时间,用正确的方法,优化正确的地方。
如果觉得这篇文章有帮助,别忘了一键三连支持一下!有任何问题或建议,欢迎在评论区交流讨论!
转载请注明出处~~~
36

被折叠的 条评论
为什么被折叠?



