性能优化要以数据为导向,先测量再优化。本节介绍渲染、布局、内存、图片和异步五大优化方向的具体实践。
一、渲染性能优化
1.1 const 构造函数(最高效优化手段)
dart
// ❌ 每次 build() 都创建新实例
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16), // 没有 const
child: Text('Static Text'), // 没有 const
);
}
// ✅ const → Flutter 复用同一实例,跳过重建
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.all(16),
child: Text('Static Text'),
);
}
规则: 只要子 Widget 的属性在编译期可知且不变,都应加 const。
1.2 RepaintBoundary(隔离重绘范围)
dart
// ❌ 动画更新时,整个页面的 RenderObject 都被标记为 dirty
class ChatPage extends StatelessWidget {
Widget build(BuildContext context) {
return Column(children: [
TypingIndicator(), // 每秒闪烁动画
MessageList(), // 大量静态消息列表
]);
}
}
// ✅ 用 RepaintBoundary 隔离,动画更新不影响 MessageList
class ChatPage extends StatelessWidget {
Widget build(BuildContext context) {
return Column(children: [
RepaintBoundary(child: TypingIndicator()), // 动画在独立层
RepaintBoundary(child: MessageList()), // 静态内容不会因动画重绘
]);
}
}
1.3 精细化 setState
dart
// ❌ 整个 StatefulWidget 都重建
class ProductPage extends StatefulWidget { ... }
class _ProductPageState extends State<ProductPage> {
bool isFavorited = false;
Widget build(BuildContext context) {
return Column(children: [
ProductHeader(), // 随 isFavorited 无谓重建
ProductDescription(), // 随 isFavorited 无谓重建
FavoriteButton( // 只有这里需要更新
isFavorited: isFavorited,
onTap: () => setState(() => isFavorited = !isFavorited),
),
]);
}
}
// ✅ 方案一:提取 FavoriteButton 为独立 StatefulWidget
// ✅ 方案二:使用 ValueNotifier + ValueListenableBuilder
class _ProductPageState extends State<ProductPage> {
final _isFavorited = ValueNotifier<bool>(false);
Widget build(BuildContext context) {
return Column(children: [
const ProductHeader(), // const,永不重建
const ProductDescription(), // const,永不重建
ValueListenableBuilder<bool>(
valueListenable: _isFavorited,
builder: (_, value, __) => FavoriteButton(
isFavorited: value,
onTap: () => _isFavorited.value = !_isFavorited.value,
),
),
]);
}
}
二、布局性能优化
2.1 避免频繁重建深层树
dart
// ❌ 将回调定义在 build() 中,每次重建都创建新函数对象
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
onTap: () => _handleTap(index), // 每次创建新闭包
);
},
);
}
// ✅ 将静态子树提取为 const 或字段
class _MyState extends State<MyWidget> {
late final Widget _staticHeader;
@override
void initState() {
super.initState();
_staticHeader = const ExpensiveHeaderWidget(); // 只创建一次
}
@override
Widget build(BuildContext context) {
return Column(children: [
_staticHeader, // 不会重建
_buildBody(),
]);
}
}
2.2 Sliver 优化长列表
dart
// ❌ Column + SingleChildScrollView:所有 Widget 一次性构建
SingleChildScrollView(
child: Column(children: manyWidgets),
)
// ✅ CustomScrollView + Sliver:按需懒加载
CustomScrollView(
slivers: [
// 折叠头部
const SliverAppBar(
expandedHeight: 200,
pinned: true,
flexibleSpace: FlexibleSpaceBar(title: Text('标题')),
),
// 分组标题(固定)
const SliverToBoxAdapter(child: CategoryHeader()),
// 网格懒加载
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) => ProductCard(product: products[index]),
childCount: products.length,
),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
),
// 列表懒加载
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => RecommendItem(item: recommendations[index]),
childCount: recommendations.length,
),
),
],
)
三、内存管理
3.1 正确 dispose 资源
dart
class _VideoPlayerState extends State<VideoPlayer> {
late VideoPlayerController _controller;
StreamSubscription? _subscription;
AnimationController? _animController;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.url));
_animController = AnimationController(vsync: this, duration: ...);
_subscription = eventStream.listen(_handleEvent);
}
@override
void dispose() {
_controller.dispose(); // ✅ 释放视频播放器
_animController?.dispose(); // ✅ 释放动画控制器
_subscription?.cancel(); // ✅ 取消流订阅
super.dispose();
}
}
3.2 图片内存优化
dart
// ✅ 限制图片缓存尺寸(避免加载超大原图)
Image.network(
imageUrl,
cacheWidth: 300, // 解码为 300 像素宽(DPR 已考虑)
cacheHeight: 300, // 避免解码 4000x4000 的原图
fit: BoxFit.cover,
)
// ✅ 控制全局图片缓存大小
void main() {
PaintingBinding.instance.imageCache.maximumSize = 200; // 最多 200 张
PaintingBinding.instance.imageCache.maximumSizeBytes = 50 << 20; // 50MB
runApp(const MyApp());
}
// ✅ 不需要时手动清理图片缓存
imageCache.clear();
imageCache.clearLiveImages();
四、图片与缓存优化
yaml
dependencies:
cached_network_image: ^3.3.1
dart
// 带缓存、占位符和错误处理的图片加载
CachedNetworkImage(
imageUrl: product.imageUrl,
width: 200,
height: 200,
fit: BoxFit.cover,
placeholder: (context, url) => const ShimmerWidget(), // 骨架屏
errorWidget: (context, url, error) => const Icon(Icons.broken_image),
memCacheWidth: 400, // 内存缓存尺寸(节省内存)
maxWidthDiskCache: 800, // 磁盘缓存最大宽度
)
五、异步与后台计算
5.1 compute(简单后台任务)
dart
// 在主线程:
final result = await compute(parseProductsJson, jsonString);
// 在 Isolate 中执行(独立线程):
List<Product> parseProductsJson(String jsonString) {
final list = jsonDecode(jsonString) as List;
return list.map((e) => Product.fromJson(e)).toList();
}
// 注意:compute 的函数必须是顶层函数或 static 方法
5.2 Isolate.spawn(复杂后台任务)
dart
Future<void> runHeavyTask() async {
final receivePort = ReceivePort();
await Isolate.spawn(
_heavyTaskEntry,
receivePort.sendPort,
);
final result = await receivePort.first;
print('Result: $result');
}
// 必须是顶层函数
void _heavyTaskEntry(SendPort sendPort) {
// 执行耗时计算
final result = _complexCalculation();
sendPort.send(result);
}
小结
| 优化方向 | 关键手段 | 效果 |
|---|---|---|
| 渲染 | const、RepaintBoundary |
减少不必要重建和重绘 |
| 布局 | Sliver 懒加载、提取静态树 |
降低初始化和重建开销 |
| 内存 | dispose 资源、控制图片缓存尺寸 | 防止内存泄漏 |
| 图片 | CachedNetworkImage、cacheWidth |
减少内存和解码开销 |
| 异步 | compute、Isolate.spawn |
耗时操作移离主线程 |
👉 下一章:八、工程化与模块化