mcpField
F
flutter-debugging

flutter-debugging

github.com/vp-k开发1453 次安装12 周前更新
代码仓库

关于 flutter-debugging

面向 Flutter 开发者的系统化调试技能,要求先查明并验证根因,再修改代码。流程包括复现问题、对比可用模式、检验假设和验证修复。

主要功能

  1. 01修复前调查根因
  2. 02稳定复现问题
  3. 03对比异常与正常模式
  4. 04逐一检验假设
  5. 05通过分析和测试验证修复

适用场景

  1. 01诊断 Widget 构建或渲染错误
  2. 02排查状态、导航或网络故障
  3. 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