Skip to content

性能优化

Flutter 应用的性能优化主要围绕 减少不必要的重建减少不必要的计算 两个核心原则展开。

优化总览

优化方向核心思路影响程度
减少重建只重建变化的部分★★★
懒加载用到时才创建★★★
图片优化合理使用缓存和尺寸★★☆
列表优化复用列表项★★☆
避免阻塞耗时操作放异步★★☆

1. 减少不必要的 Widget 重建

这是 Flutter 性能优化中最重要的一条规则。

使用 const 构造函数

dart
// ❌ 每次父组件重建时,Text 也会重建
Text('Hello')

// ✅ 编译时常量,不会重建
const Text('Hello')
dart
// ❌ 列表项没有 const
ListView(
  children: [
    Text('Item 1'),
    Text('Item 2'),
    Text('Item 3'),
  ],
)

// ✅ 列表项使用 const
ListView(
  children: const [
    Text('Item 1'),
    Text('Item 2'),
    Text('Item 3'),
  ],
)

拆分 Widget,缩小重建范围

dart
// ❌ 整个页面重建,即使只有计数器变化
class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final count = context.watch<Counter>().count;

    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),   // 不需要重建
      body: Center(child: Text('$count')),              // 需要重建
      bottomNavigationBar: BottomAppBar(child: ...),    // 不需要重建
    );
  }
}

// ✅ 只有计数器部分重建
class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: const _CounterBody(),    // 独立 Widget,只有它监听变化
      bottomNavigationBar: BottomAppBar(child: ...),
    );
  }
}

class _CounterBody extends StatelessWidget {
  const _CounterBody();

  @override
  Widget build(BuildContext context) {
    final count = context.watch<Counter>().count;  // 只在这里监听
    return Center(child: Text('$count'));
  }
}

使用 Selector 精确监听

dart
// ❌ 整个 User 对象变化时都重建,即使只用到了 name
context.watch<User>();

// ✅ 只有 name 变化时才重建
context.select<User, String>((user) => user.name);

2. 列表优化

使用 ListView.builder

dart
// ❌ 一次性创建所有子项,数据量大时卡顿
ListView(
  children: List.generate(1000, (i) => Text('Item $i')),
)

// ✅ 懒加载,只创建可见项
ListView.builder(
  itemCount: 1000,
  itemBuilder: (context, index) => Text('Item $index'),
)

给列表项添加 Key

dart
// ✅ 列表项有唯一 Key,Flutter 可以高效复用
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      key: ValueKey(item.id),   // 唯一标识
      title: Text(item.name),
    );
  },
)

避免在 itemBuilder 中创建对象

dart
// ❌ 每次滚动都新建 Color 和 TextStyle
itemBuilder: (context, index) {
  return Text(
    'Item $index',
    style: TextStyle(color: Colors.red, fontSize: 16),  // 每次新建
  );
}

// ✅ 提取为常量或成员变量
static const _itemStyle = TextStyle(color: Colors.red, fontSize: 16);

itemBuilder: (context, index) {
  return Text('Item $index', style: _itemStyle);
}

3. 图片优化

dart
// ❌ 直接用 Image.network,无缓存、无占位
Image.network('https://example.com/large-photo.jpg')

// ✅ 使用 cached_network_image
CachedNetworkImage(
  imageUrl: 'https://example.com/large-photo.jpg',
  placeholder: (_, __) => const CircularProgressIndicator(),
  errorWidget: (_, __, ___) => const Icon(Icons.error),
  memCacheWidth: 300,   // 限制内存缓存尺寸(像素),避免加载原始大图
)

图片优化要点

  • 使用 memCacheWidth/memCacheHeight:加载 4000x3000 的原图到 300 宽的容器中是浪费内存,限制缓存尺寸可以大幅降低内存占用
  • 使用 WebP 格式:比 PNG/JPG 体积更小
  • 提供缩略图:列表中使用小图,详情页使用大图

4. 避免阻塞主线程

dart
// ❌ 在主线程做耗时 JSON 解析
void loadData() async {
  final response = await dio.get('/large-data');
  final data = jsonDecode(response.data);  // 大数据时可能卡顿
  setState(() => _data = data);
}

// ✅ 耗时计算放到 Isolate
void loadData() async {
  final response = await dio.get('/large-data');
  final data = await compute(jsonDecode, response.data);  // 在后台线程解析
  setState(() => _data = data);
}

何时使用 Isolate?

  • JSON 数据超过 100KB
  • 图片处理(裁剪、滤镜)
  • 加密/解密
  • 复杂排序/计算

一般网络请求、UI 操作不需要 Isolate。

5. 懒加载

页面懒加载

dart
// ❌ 一次性加载所有 Tab 页面
TabBarView(
  children: [
    HomePage(),      // 立即创建
    SearchPage(),    // 立即创建
    ProfilePage(),   // 立即创建
  ],
)

// ✅ 使用 LazyLoad 或只在可见时加载
TabBarView(
  children: [
    HomePage(),
    SearchPage(),
    ProfilePage(),
  ],
)
// TabBarView 默认是懒加载的,但会预缓存相邻页面
// 可通过 PageController 设置 cacheExtent 控制预缓存范围

图片懒加载

dart
// ✅ 列表中的图片只在即将可见时加载
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return CachedNetworkImage(
      imageUrl: items[index].imageUrl,
      // cached_network_image 自动实现懒加载
    );
  },
)

6. 资源释放

dart
// ❌ 忘记释放 Controller
class _MyState extends State<MyWidget> {
  final _controller = TextEditingController();

  // 没有 dispose!→ 内存泄漏
}

// ✅ 在 dispose 中释放所有资源
class _MyState extends State<MyWidget> {
  late final TextEditingController _controller;
  late final ScrollController _scrollController;
  StreamSubscription? _subscription;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
    _scrollController = ScrollController();
    _subscription = someStream.listen((_) {});
  }

  @override
  void dispose() {
    _controller.dispose();
    _scrollController.dispose();
    _subscription?.cancel();
    super.dispose();
  }
}

性能分析工具

Flutter DevTools

bash
# 启动 DevTools
flutter pub global activate devtools
flutter pub global run devtools

# 或在运行应用时按终端提示的链接打开
flutter run

常用分析面板

面板用途
Widget Inspector查看 Widget 树,检查布局问题
Performance分析帧率,找出卡顿原因
CPU Profiler分析 CPU 使用,找出耗时函数
Memory分析内存使用,检测内存泄漏
Network查看网络请求

检查重建次数

dart
// 在 Widget 中添加 debugPrint,观察重建频率
@override
Widget build(BuildContext context) {
  debugPrint('${widget.runtimeType} rebuilt');  // 开发时使用
  return ...;
}

提示

debugPrint 仅在 Debug 模式有效,Release 模式会自动移除。上线前记得删除或改用日志框架。

性能优化检查清单

  • [ ] 可用 const 的地方都用了 const
  • [ ] 长列表使用 ListView.builder 而非 ListView(children: [...])
  • [ ] 图片使用 CachedNetworkImage 并设置 memCacheWidth
  • [ ] 耗时操作使用 compute 放到 Isolate
  • [ ] Controller、Subscription 在 dispose 中释放
  • [ ] context.watch 精确到最小范围
  • [ ] 列表项有唯一 key
  • [ ] 使用 DevTools 验证无内存泄漏

下一步

基于 Flutter 官方文档整理