14_调试与DevTools
Flutter 调试技巧和 DevTools 使用
Flutter DevTools、调试技巧、常见性能问题排查。
Flutter DevTools
# 运行时自动可用,VS Code 调试时会弹出链接
# 或手动启动
dart devtools
功能面板对比 Chrome DevTools:
| Flutter DevTools 面板 | 功能 | 对比 Chrome DevTools |
|---|---|---|
| Widget Inspector | 查看 Widget 树、检查布局盒子 | Elements 面板 |
| Performance | 帧率图、UI 线程 / GPU 线程耗时 | Performance 面板 |
| CPU Profiler | 函数调用链、耗时分析 | Profiler |
| Memory | 内存快照、泄漏对象检测 | Memory 面板 |
| Network | 网络请求监控(需 dio 配合) | Network 面板 |
| Logging | 日志查看 | Console |
调试常用技巧
// 打印(推荐 debugPrint,自动截断长日志)
debugPrint('调试信息: $value');
print('快速调试(开发时)');
// 断言(仅 Debug 模式生效,不影响生产性能)
assert(count >= 0, 'count 不能为负数');
assert(user != null, '用户对象不应为 null');
// 检查运行模式
import 'package:flutter/foundation.dart';
if (kDebugMode) { print('debug mode'); }
if (kReleaseMode) { /* 生产逻辑 */ }
if (kProfileMode) { /* 性能分析模式 */ }
// 性能覆盖层(显示帧率和线程耗时条)
MaterialApp(
showPerformanceOverlay: kDebugMode, // 开发时显示
debugShowCheckedModeBanner: false, // 隐藏右上角 DEBUG 标志
)
Widget Inspector 使用
- 打开 DevTools → Widget Inspector
- 点击 Select Widget 按钮
- 在模拟器/设备上点击任意 UI 元素
- 右侧面板显示:Widget 类型、属性值、父子关系
常用场景:
- 查看某个组件的实际 padding/margin
- 找出某个元素的 Widget 类型(方便查文档)
- 查看 Widget 树层级(层级过深可能影响性能)
Performance 面板使用
读懂火焰图:
- UI Thread(上半部分):Dart 代码执行,包含 build / layout / paint
- Raster Thread(下半部分):GPU 合成,把 Canvas 指令渲染到屏幕
- 红色卡顿帧:超过 16ms(60fps)或 8ms(120fps)的帧,需要优化
判断卡顿来源:
- UI Thread 过长 → Dart 代码逻辑太慢,减少 build 中的计算
- Raster Thread 过长 → 合成层太多、图片太大、复杂 shader
常见性能问题排查
// ❌ 问题一:在 build() 中做耗时计算(每次重建都计算)
Widget build(BuildContext context) {
final sorted = List.from(bigList)..sort(); // ❌ 每次都排序!
return ListView.builder(...);
}
// ✅ 解决:在 initState 或状态变化时计算一次
@override
void initState() {
super.initState();
_sortedList = List.from(bigList)..sort(); // ✅ 只算一次
}
// ❌ 问题二:整个大组件因局部数据变化而全部重建
Widget build(BuildContext context) {
final cart = context.watch<CartModel>(); // 监听整个 CartModel
return Scaffold(
appBar: AppBar(title: const Text('商店')), // 不用 cart,也被重建了
body: CartList(cart: cart),
);
}
// ✅ 解决:用 Consumer 或 context.select 缩小重建范围
Widget build(BuildContext context) {
return Scaffold(
appBar: const AppBar(title: Text('商店')), // const,永不重建
body: Consumer<CartModel>(
builder: (ctx, cart, _) => CartList(cart: cart),
),
);
}
// ❌ 问题三:大列表不用 builder
ListView(
children: bigList.map((e) => ItemWidget(e)).toList(), // 一次性渲染全部!
)
// ✅ 解决:ListView.builder 按需渲染
ListView.builder(itemCount: bigList.length, itemBuilder: ...)
// ❌ 问题四:AnimatedBuilder 没有用 child 参数(静态部分重复重建)
AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Transform.rotate(
angle: _controller.value * 6.28,
child: const Icon(Icons.refresh, size: 48), // ❌ 每帧都重建 Icon
);
},
)
// ✅ 解决:静态部分通过 child 传入(只创建一次)
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _controller.value * 6.28,
child: child, // ✅ 复用,不重建
);
},
child: const Icon(Icons.refresh, size: 48),
)
内存泄漏排查
// 最常见的泄漏来源:Controller / Subscription 没有 dispose
class _MyState extends State<MyWidget> {
final _textController = TextEditingController();
final _animController = AnimationController(vsync: this, ...);
final _scrollController = ScrollController();
StreamSubscription? _subscription;
Timer? _timer;
// ✅ 全部在 dispose 中释放
@override
void dispose() {
_textController.dispose();
_animController.dispose();
_scrollController.dispose();
_subscription?.cancel();
_timer?.cancel();
super.dispose();
}
}
使用 Memory 面板检测泄漏:
- DevTools → Memory
- 执行可能泄漏的操作(如反复打开关闭某页面)
- 点击 GC(强制垃圾回收)
- 查看对象数量是否持续增加
常用 Flutter 命令
# 分析代码问题(类似 ESLint)
flutter analyze
# 格式化代码(类似 Prettier)
dart format lib/
# 查看当前依赖树
flutter pub deps
# 清理构建缓存(遇到奇怪错误时用)
flutter clean && flutter pub get
# 查看 app 大小分析
flutter build apk --analyze-size