# Flutter Sketch 还原：容器溢出案例

> 案例 ID: `sketch-overflow-container`  
> 日期: 2026-01-26  
> 项目: my_flutter 账户余额页面  
> 严重程度: ⚠️ 高（页面直接报错）

## 📋 问题描述

### 报错信息

```
BOTTOM OVERFLOWED BY 14 PIXELS (余额卡片)
BOTTOM OVERFLOWED BY 1.00 PIXELS (空状态卡片)
```

### 现象截图

余额卡片底部按钮被截断，空状态卡片图标和文字布局异常。

---

## 🔍 诊断过程

### 步骤 1：测量设计稿

使用 Sketch MCP 工具测量「账户余额 - 空状态」画板：

```javascript
// 余额卡片测量结果
{
  "BalanceCard": {
    "y": 100, "width": 350, "height": 140
  },
  "WalletIcon": { "相对卡片顶部": 20, "高度": 48 },
  "RechargeButton": { "相对卡片底部": 8, "高度": 36 }
}

// 空状态卡片测量结果
{
  "EmptyStateCard": { "height": 280 },
  "图标距顶": 40,
  "图标高度": 100,
  "图标到标题": 20,
  "标题高度": 25,
  "标题到描述": 7,
  "描述高度": 20,
  "描述到按钮": 16,
  "按钮高度": 40,
  "按钮距底": 12
}
```

### 步骤 2：计算内容高度

**余额卡片内容计算：**
```
padding.top:     20px
钱包图标:        48px
间距:            ? (需要推算)
按钮:            36px
padding.bottom:   8px
-----------------------
基础总计:        112px + 间距
```

**问题发现**：设计稿容器 140px，但设置了 `height: 1.2` 的文本，导致文本实际高度比字号大 20%。

### 步骤 3：审查代码

```dart
// 问题代码 1：固定高度
Container(
  height: 140,  // 直接使用 Sketch 测量值
  child: Column(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      // 内容
      Row(children: [按钮, 按钮])  // 按钮内文本 height: 1.2
    ],
  ),
)

// 问题代码 2：symmetric padding
Container(
  height: 280,
  padding: EdgeInsets.symmetric(vertical: 12),  // 12 + 12 = 24px!
  child: Column(
    children: [
      SizedBox(height: 28),  // 期望 12+28=40 距顶，✓
      // 但底部也有 12px padding，压缩了可用空间
    ],
  ),
)
```

### 步骤 4：根因确认

| 问题 | 根因 | 溢出量 |
|-----|------|-------|
| 余额卡片 | 固定 height=140 + 文本 height:1.2 导致额外空间 | 14px |
| 空状态卡片 | symmetric(vertical:12) 占用 24px 而非 12px | 1px |

---

## ✅ 解决方案

### 修复 1：余额卡片 - 移除固定高度

```dart
// ❌ 修复前
Container(
  height: 140,
  decoration: ...,
  child: Column(...),
)

// ✅ 修复后
Container(
  // 不设置 height，让内容决定高度
  decoration: ...,
  child: Column(
    mainAxisSize: MainAxisSize.min,  // 最小适应内容
    children: [
      SizedBox(height: 20),  // 顶部间距
      WalletIcon(),
      Spacer(),  // 弹性间距
      ButtonRow(),
      SizedBox(height: 8),  // 底部间距
    ],
  ),
)
```

### 修复 2：空状态卡片 - 精确控制间距

```dart
// ❌ 修复前
Container(
  height: 280,
  padding: EdgeInsets.symmetric(vertical: 12),  // 双倍陷阱
  child: Column(
    children: [
      SizedBox(height: 28),  // 12+28=40 ✓
      // ...
    ],
  ),
)

// ✅ 修复后
Container(
  height: 280,
  // 移除 vertical padding
  child: Column(
    children: [
      SizedBox(height: 40),  // 直接设置距顶 40px
      EmptyIcon(),
      SizedBox(height: 20),
      Title(),
      SizedBox(height: 7),
      Description(),
      SizedBox(height: 16),
      ActionButton(),
      SizedBox(height: 12),  // 距底 12px
    ],
  ),
)
```

---

## 📚 经验总结

### 核心教训

1. **不要盲信 Sketch 测量的容器高度**
   - 测量值是「设计意图」，不是「代码约束」
   - 必须计算内容是否能装下

2. **EdgeInsets.symmetric 是双刃剑**
   - `vertical: X` = 顶部 X + 底部 X = 2X
   - 精确布局时用 `EdgeInsets.only`

3. **文本行高会影响布局**
   - `height: 1.2` 让文本高度 = fontSize × 1.2
   - 按钮内文本尤其容易被忽略

### 预防措施

- [ ] 还原前计算内容总高度
- [ ] 使用 `MainAxisSize.min` 让容器自适应
- [ ] 避免在精确布局中使用 `symmetric`
- [ ] 注意文本 `height` 属性的影响

---

## 🔗 相关链接

- [05-common-pitfalls.md](../../standards/sketch/05-common-pitfalls.md) - 陷阱 1 & 2
- [03-flutter-mapping.md](../../standards/sketch/03-flutter-mapping.md) - Flutter 映射规范
