关于 flutter-debugging
面向 Flutter 开发者的系统化调试技能,要求先查明并验证根因,再修改代码。流程包括复现问题、对比可用模式、检验假设和验证修复。
主要功能
- 01修复前调查根因
- 02稳定复现问题
- 03对比异常与正常模式
- 04逐一检验假设
- 05通过分析和测试验证修复
适用场景
- 01诊断 Widget 构建或渲染错误
- 02排查状态、导航或网络故障
- 03追查构建失败或测试失败
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ vp-k/ flutter-craft --skill flutter-debugging安装后在 Claude Code 中输入/flutter-debugging确认已加载。
详细介绍
Flutter 系统化调试
概述
随意修复会浪费时间,还会引入新 Bug。快速打补丁会掩盖根本问题。
核心原则: 尝试修复之前,务必找到根本原因。只修复症状就是失败。
开始时声明:“我正在使用 flutter-debugging 技能调查此问题。”
铁律
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
如果你尚未完成阶段 1,就不能提出修复方案。
适用场景
适用于任何 Flutter 技术问题:
- Widget 构建错误
- 状态管理 Bug
- 测试失败
- UI 渲染问题
- 导航问题
- API/网络错误
- 构建失败
- 特定平台的问题
以下情况尤其要使用:
- “只要快速修一下”似乎显而易见
- 你已经尝试过多种修复方法
- 先前的修复没有奏效
- 时间紧迫
Flutter 调试工具
1. 控制台日志
// Basic logging (truncates long output)
print('Debug: $value');
// No truncation (preferred)
debugPrint('Debug: $value');
// Conditional debug logging
import 'package:flutter/foundation.dart';
if (kDebugMode) {
debugPrint('Only in debug mode: $value');
}
// Developer log with tags
import 'dart:developer';
log('API response', name: 'NetworkService', error: e);
2. Flutter DevTools
# Open DevTools
flutter run --start-paused
# Then press 'd' or use DevTools button in IDE
DevTools 面板:
- Widget 检查器: Widget 树、属性、布局
- 性能: 时间线、帧渲染
- 内存: 堆使用情况、内存泄漏
- 网络: HTTP 请求/响应
- 日志记录: 集中查看所有日志
3. 调试 Widget
// Show widget boundaries
debugPaintSizeEnabled = true;
// Show baseline alignments
debugPaintBaselinesEnabled = true;
// Show repaint regions
debugRepaintRainbowEnabled = true;
4. 断点
// Programmatic breakpoint
import 'dart:developer';
debugger();
// Or use IDE breakpoints
四个阶段
阶段 1:调查根本原因
尝试任何修复之前:
1. 仔细阅读错误消息
════════════════════════════════════════════════════════════
EXCEPTION CAUGHT BY WIDGETS LIBRARY
════════════════════════════════════════════════════════════
The following assertion was thrown building MyWidget:
'package:flutter/src/widgets/container.dart': Failed assertion: line 287
'child != null || decoration != null || constraints != null'
════════════════════════════════════════════════════════════
不要跳过错误! 错误消息通常包含确切的解决方案。
2. 稳定复现问题
# Hot reload to reproduce
r # in terminal
# Hot restart (clears state)
R # in terminal
# Full restart
flutter run
- 你能稳定触发问题吗?
- 具体步骤是什么?
- 热重载和热重启是否会影响结果?
3. 检查近期改动
# What changed recently?
git diff
# Recent commits
git log --oneline -10
# Diff with specific commit
git diff HEAD~3
4. 添加诊断日志
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint('=== MyWidget.build() ===');
debugPrint('context.mounted: ${context.mounted}');
final state = context.watch<MyState>();
debugPrint('state: $state');
return Container(...);
}
}
阶段 2:分析模式
1. 查找可正常运行的示例
# Search for similar working code
grep -r "similar_pattern" lib/
- 与出问题的部分相似的功能中,哪些可以正常运行?
- 比较 Widget 结构、状态管理
2. 对照参考实现
// Reference: Working widget
class WorkingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<WorkingBloc, WorkingState>(
builder: (context, state) => ...,
);
}
}
// Broken: My widget
class BrokenWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// What's different?
return BlocBuilder<MyBloc, MyState>(
builder: (context, state) => ...,
);
}
}
3. 找出差异
列出所有差异,无论多小:
- Widget 类型
- 状态提供者的位置
- BuildContext 的用法
- 生命周期方法
阶段 3:提出假设并进行测试
1. 提出单一假设
清楚地说明:
“我认为问题出在 [X],因为 [Y]”
示例:
“我认为构建错误是因为 Widget 树中此 Widget 上方没有提供 BLoC”
2. 尽可能少地进行测试
做出尽可能小的改动:
// Before (broken)
Widget build(BuildContext context) {
final bloc = context.read<MyBloc>();
return ...;
}
// After (testing hypothesis)
Widget build(BuildContext context) {
debugPrint('Looking for MyBloc...');
try {
final bloc = context.read<MyBloc>();
debugPrint('Found: $bloc');
} catch (e) {
debugPrint('Error: $e');
}
return ...;
}
3. 继续之前先验证
- 问题解决了吗?→ 阶段 4
- 没解决?→ 提出新假设
- 不要在现有修复上叠加更多修复
阶段 4:实施
1. 创建测试用例(根据优先级选做)
对于 Repository/DataSource 问题(优先级 1):
test('should return user when API succeeds', () async {
// Arrange
when(mockApi.getUser(any)).thenAnswer((_) async => userModel);
// Act
final result = await repository.getUser('123');
// Assert
expect(result, equals(userEntity));
});
2. 实施单一修复
一次只做一项改动:
// Fix the root cause identified in Phase 3
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => getIt<MyBloc>(),
child: BlocBuilder<MyBloc, MyState>(...),
);
}
}
3. 验证修复
# Run analysis
flutter analyze
# Run tests
flutter test
# Manual verification
flutter run
# Test the specific scenario
4. 如果修复无效
- 计数:你尝试过多少种修复方法?
- 如果少于 3 种:返回阶段 1,重新分析
- 如果大于或等于 3 种:停止并质疑架构
常见 Flutter 问题及根本原因
| 症状 | 常见根本原因 |
|---|---|
| “未找到祖先” | Provider/BLoC 不在上方的 Widget 树中 |
| “dispose 后调用 setState” | Widget 已销毁后,异步操作才完成 |
| “RenderBox 尚未布局” | 约束无界(例如 Column 嵌套在 Column 中) |
| “空值检查运算符” | 变量未初始化,或 API 返回了 null |
| “构建期间触发构建” | 在构建阶段调用了 setState |
| “Ticker 未销毁” | 缺少 with TickerProviderStateMixin 或 dispose |
危险信号——停止并遵循流程
如果你发现自己在想:
- “先快速修一下”
- “用 try-catch 包起来就行”
- “在这里添加空值检查”
- “给它套一个 Container 就行”
- “也许只要热重启”
- “问题大概出在状态上,我重置一下”
以上任何想法都意味着:停止。返回阶段 1。
必需的子技能
修复 Bug 后,你必须调用: → flutter-craft:flutter-verification
使用 flutter analyze、flutter test 和手动测试验证修复。
快速参考
| 阶段 | Flutter 活动 | 成功标准 |
|---|---|---|
| 1. 根本原因 | 阅读错误信息、复现问题、添加 debugPrint | 理解是什么以及为什么 |
| 2. 模式 | 查找可正常工作的组件并进行比较 | 找出差异 |
| 3. 假设 | 提出理论并进行最小化测试 | 得到确认或提出新假设 |
| 4. 实现 | 创建测试(按优先级排序)、修复并验证 | Bug 已解决,分析结果正常 |
为什么要系统化
随机修复往往会陷入反复折腾:每个失败的猜测都会污染代码,并掩盖原始症状。先找出根本原因再调试,能将改动保持在最小范围,让修复可验证,并避免在“尝试各种办法”时引入新 Bug。
评论
支持 Markdown