# Sketch 列表 Item 区域高度不一致问题

> **问题标签**: `sketch`, `list`, `menu`, `item`, `padding`, `divider`  
> **框架**: Flutter  
> **严重程度**: 中等（导致列表布局细节偏差）

---

## 🔍 问题识别

### 用户描述关键词
- "第一个/最后一个 item 高度不对"
- "列表 padding 不均匀"
- "divider 位置偏移"
- "底部/顶部留白太多/太少"

### 问题特征
- [ ] 列表首尾 item 与中间 item 高度不同
- [ ] 使用统一 padding 导致视觉不平衡
- [ ] 忽略 divider 在高度计算中的影响
- [ ] 假设所有 item 高度相同

---

## 🎯 核心原理

**根本原因**：设计稿中列表的首个和末尾 item 通常有不同的 padding，以平衡与容器边缘的视觉关系。

### 典型 Sketch 测量结果

```
Menu Card_background: 350px

Divider 位置:
- Divider 1: y=63
- Divider 2: y=119  
- Divider 3: y=175
- Divider 4: y=231
- Divider 5: y=287

区域高度计算:
- Item 1: 0 → 63 = 63px
- Item 2: 63 → 119 = 56px
- Item 3: 119 → 175 = 56px
- Item 4: 175 → 231 = 56px
- Item 5: 231 → 287 = 56px
- Item 6: 287 → 350 = 63px
```

**结论**: 首尾 item 63px，中间 item 56px，**不是**统一高度！

---

## ❌ 常见错误

```dart
// ❌ 错误: 统一 padding
Padding(
  padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
  child: MenuItem(),
)
```

---

## ✅ 正确解决方案

### 1. 测量 Icon 精确位置

```javascript
// 测量所有 icon 的 y 坐标
const icons = sketch.find('[name*="Icon BG"]', menuCard);
icons.forEach(icon => {
  if (icon.frame.height === 36) {
    console.log(`${icon.name}: y=${icon.frame.y}`);
  }
});

// 结果:
// Item 1 Icon: y=20
// Item 2 Icon: y=76
// Item 3 Icon: y=132
// ...
```

### 2. 计算每个 item 的 padding

```
Item 1 (区域 0→63):
- region_start = 0
- icon_y = 20 → top = 20
- icon_bottom = 56
- divider_y = 63 → bottom = 63 - 56 - 1 = 6
- 验证: 20 + 36 + 6 + 1 = 63 ✅

Item 2 (区域 64→119):
- region_start = 64 (divider1 + 1)
- icon_y = 76 → top = 76 - 64 = 12
- icon_bottom = 112
- divider_y = 119 → bottom = 119 - 112 - 1 = 6
- 验证: 12 + 36 + 6 + 1 = 55... 

等等，56 - 1(divider) = 55，但 12+36+6=54？
让我重新计算：区域 = 56px 含 divider
内容区 = 55px，12 + 36 + 7 = 55 ✅
```

### 3. Flutter 精确实现

```dart
Widget _buildMenuItem(int index, MenuItem item) {
  // Sketch 测量 (背景层 350px):
  // - Item 1: 区域 63px = top:20 + icon:36 + bottom:6 + divider:1
  // - Item 2-5: 区域 56px = top:12 + icon:36 + bottom:7 + divider:1  
  // - Item 6: 区域 63px = top:12 + icon:36 + bottom:15 (无 divider)
  
  final isFirst = index == 0;
  final isLast = index == 5; // 6 个 item
  
  return Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      Padding(
        padding: EdgeInsets.fromLTRB(
          16,
          isFirst ? 20 : 12,              // 首 item top 更大
          16,
          isFirst ? 6 : (isLast ? 15 : 7), // 尾 item bottom 更大
        ),
        child: Row(
          children: [
            Icon(size: 36),
            SizedBox(width: 16),
            Expanded(child: Text(item.title)),
            ArrowIcon(),
          ],
        ),
      ),
      
      // Divider (最后一个不需要)
      if (!isLast)
        Container(
          margin: EdgeInsets.only(left: 68, right: 20),
          height: 1,
          color: Color(0x0F1C2B45),
        ),
    ],
  );
}
```

---

## 🔑 关键公式

### 从 Sketch 测量计算 padding

```
top_padding = icon.y - region_start
bottom_padding = region_end - icon.bottom - divider_height
```

其中:
- `region_start` = 上一个 divider.y + 1 (首 item 为 0)
- `region_end` = 当前 divider.y (尾 item 为 background.height)
- `icon.bottom` = icon.y + icon.height
- `divider_height` = 1 (尾 item 为 0)

### 验证公式

```
区域高度 = top_padding + icon_height + bottom_padding + divider_height
```

---

## 📊 真实案例数据

### Profile Menu Card (350px)

| Item | 区域 | Top | Icon | Bottom | Divider | 总计 |
|------|------|-----|------|--------|---------|------|
| 1 | 0→63 | 20 | 36 | 6 | 1 | 63 |
| 2 | 63→119 | 12 | 36 | 7 | 1 | 56 |
| 3 | 119→175 | 12 | 36 | 7 | 1 | 56 |
| 4 | 175→231 | 12 | 36 | 7 | 1 | 56 |
| 5 | 231→287 | 12 | 36 | 7 | 1 | 56 |
| 6 | 287→350 | 12 | 36 | 15 | 0 | 63 |

**总计**: 63 + 56×4 + 63 = 350px ✅

---

## ⚠️ 预防措施

1. **不要假设统一高度**: 始终测量首、中、尾三种 item
2. **测量 icon 实际 y 坐标**: 不要只看区域高度
3. **区分有无 divider**: 尾 item 通常无 divider
4. **使用背景层边界**: 不要用 Frame 高度

---

## 🔗 相关问题

- [sketch-背景层高度.md](./sketch-背景层高度.md) - 背景层 vs Frame 高度
- [layout-尺寸不匹配.md](./layout-尺寸不匹配.md) - 通用布局问题

---

**创建日期**: 2026-01-19  
**最后更新**: 2026-01-19  
**来源**: Profile 页面 Menu Item 布局调试实战
