mta-mcp 2.13.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/flutter.agent.md +117 -1147
- package/agents/vue3.agent.md +177 -464
- package/dist/index.d.ts +63 -0
- package/dist/index.js +551 -132
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/troubleshooting/README.md +366 -0
- package/troubleshooting/USAGE_GUIDE.md +289 -0
- package/troubleshooting/flutter/clip-/351/230/264/345/275/261/350/243/201/345/211/252.md +244 -0
- package/troubleshooting/flutter/input-/345/255/227/346/256/265/347/274/272/345/244/261.md +240 -0
- package/troubleshooting/flutter/input-/350/276/271/346/241/206/351/227/256/351/242/230.md +236 -0
- package/troubleshooting/flutter/layout-/345/260/272/345/257/270/344/270/215/345/214/271/351/205/215.md +214 -0
- package/troubleshooting/flutter/shadow-/351/200/217/345/207/272/351/227/256/351/242/230.md +172 -0
- package/troubleshooting/flutter/sketch-/345/233/276/346/240/207/345/260/272/345/257/270.md +135 -0
- package/troubleshooting/flutter/sketch-/345/256/214/346/225/264/346/217/220/345/217/226.md +201 -0
- package/troubleshooting/flutter/sketch-/345/261/236/346/200/247/346/234/252/344/275/277/347/224/250.md +139 -0
- package/troubleshooting/flutter/svg-/346/234/252/345/261/205/344/270/255.md +120 -0
- package/troubleshooting/flutter/svg-/351/242/234/350/211/262/345/274/202/345/270/270.md +117 -0
- package/troubleshooting/flutter/tabbar-/345/212/250/347/224/273/345/220/214/346/255/245.md +107 -0
- package/troubleshooting/flutter/withopacity-/345/274/203/347/224/250.md +81 -0
- package/troubleshooting/vue3/cascader-/350/257/257/346/233/277/346/215/242.md +130 -0
- package/troubleshooting/vue3/drawer-input-/346/240/267/345/274/217.md +181 -0
- package/troubleshooting/vue3/table-/347/274/226/350/276/221/345/217/226/346/266/210.md +148 -0
- package/troubleshooting/vue3/table-/350/276/271/346/241/206/351/227/256/351/242/230.md +178 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Flutter 布局尺寸不匹配问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `layout`, `sizing`, `design-spec`, `position`, `padding`
|
|
4
|
+
> **问题类型**: 布局偏差
|
|
5
|
+
> **框架**: Flutter
|
|
6
|
+
> **严重程度**: 中等
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🔍 问题识别
|
|
11
|
+
|
|
12
|
+
### 自动检测特征
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
// 代码模式匹配 - 使用自动布局
|
|
16
|
+
Row(
|
|
17
|
+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
18
|
+
children: [...]
|
|
19
|
+
)
|
|
20
|
+
// 或
|
|
21
|
+
Expanded(child: ...)
|
|
22
|
+
// 或缺少明确的尺寸
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 用户描述关键词
|
|
26
|
+
- "位置跟设计稿不一样"
|
|
27
|
+
- "间距不对"
|
|
28
|
+
- "元素偏移了"
|
|
29
|
+
- "尺寸不匹配"
|
|
30
|
+
- "布局差不多但不精确"
|
|
31
|
+
|
|
32
|
+
### 问题特征
|
|
33
|
+
- [ ] 输入框内元素间距与设计稿不符
|
|
34
|
+
- [ ] 国旗/图标/分隔线位置偏移
|
|
35
|
+
- [ ] 容器宽高与设计稿不一致
|
|
36
|
+
- [ ] 边框粗细或圆角不对
|
|
37
|
+
- [ ] 使用了 `MainAxisAlignment` 自动分布
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
42
|
+
|
|
43
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
44
|
+
|----------|-----------|---------|
|
|
45
|
+
| 调整 padding/margin 值 | 依赖猜测,无法精确 | 2-3 轮对话 |
|
|
46
|
+
| 使用 `MainAxisAlignment.spaceBetween` | 间距不可控 | 1-2 轮对话 |
|
|
47
|
+
| 依赖 `Expanded` 自动填充 | 宽度与设计稿不符 | 1-2 轮对话 |
|
|
48
|
+
| 忽略设计稿像素值 | 总是"差不多"但不精确 | 持续问题 |
|
|
49
|
+
|
|
50
|
+
**总计浪费**: 4-7 轮对话
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## ✅ 正确解决方案
|
|
55
|
+
|
|
56
|
+
### 核心原理
|
|
57
|
+
|
|
58
|
+
**问题根源**:未从设计稿提取精确坐标,依赖 Flutter 的自动布局系统。
|
|
59
|
+
|
|
60
|
+
Flutter 的 `Row`、`MainAxisAlignment`、`Expanded` 等会自动分配空间,但这种"自动"无法保证与设计稿完全一致。
|
|
61
|
+
|
|
62
|
+
### 解决步骤
|
|
63
|
+
|
|
64
|
+
#### 1. 从设计稿提取精确坐标
|
|
65
|
+
|
|
66
|
+
使用 Sketch MCP 工具或设计软件获取每个元素的精确位置:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
设计稿规格示例 (国家代码选择器):
|
|
70
|
+
- 容器: 290x54, 圆角16
|
|
71
|
+
- 国旗 emoji: x=12, fontSize=20
|
|
72
|
+
- +61 文字: x=40, fontSize=15, fontWeight=600
|
|
73
|
+
- 下拉箭头: x=76, size=16x16
|
|
74
|
+
- 分隔线: x=98, 1x34
|
|
75
|
+
- 输入区起点: x=110
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### 2. 使用固定宽度而非弹性布局
|
|
79
|
+
|
|
80
|
+
```dart
|
|
81
|
+
// ❌ 错误:依赖自动布局
|
|
82
|
+
Row(
|
|
83
|
+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
84
|
+
children: [国旗, 区号, 箭头, 分隔线, 输入框],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
// ✅ 正确:固定宽度容器
|
|
88
|
+
Row(
|
|
89
|
+
children: [
|
|
90
|
+
Container(
|
|
91
|
+
width: 98, // 精确到分隔线位置 (设计稿 x=98)
|
|
92
|
+
padding: const EdgeInsets.only(left: 12), // 国旗起点 x=12
|
|
93
|
+
child: Row(
|
|
94
|
+
children: [
|
|
95
|
+
const Text('🇦🇺', style: TextStyle(fontSize: 20)),
|
|
96
|
+
const SizedBox(width: 5), // 间距精确计算:40 - 12 - 20 = 8(约5)
|
|
97
|
+
const Text(
|
|
98
|
+
'+61',
|
|
99
|
+
style: TextStyle(
|
|
100
|
+
fontSize: 15,
|
|
101
|
+
fontWeight: FontWeight.w600,
|
|
102
|
+
color: Color(0xFF1C2B45),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
const SizedBox(width: 4), // 间距:76 - 40 - 文字宽度
|
|
106
|
+
const Icon(Icons.keyboard_arrow_down, size: 16, color: Color(0x44CCCCCC)),
|
|
107
|
+
],
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
Container(
|
|
111
|
+
width: 1,
|
|
112
|
+
height: 34,
|
|
113
|
+
color: const Color(0x331C2B45)
|
|
114
|
+
), // 分隔线
|
|
115
|
+
Expanded(child: TextField(...)), // 输入区从 x=110 开始
|
|
116
|
+
],
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### 3. 精确设置边框和圆角
|
|
121
|
+
|
|
122
|
+
```dart
|
|
123
|
+
decoration: BoxDecoration(
|
|
124
|
+
borderRadius: BorderRadius.circular(16), // 设计稿圆角值
|
|
125
|
+
border: Border.all(
|
|
126
|
+
color: const Color(0x99FFFFFF), // 设计稿边框颜色
|
|
127
|
+
width: 1, // 设计稿边框粗细
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 📋 精确还原检查清单
|
|
135
|
+
|
|
136
|
+
### 必须从设计稿读取的值
|
|
137
|
+
|
|
138
|
+
- [ ] **容器尺寸**: width, height
|
|
139
|
+
- [ ] **圆角**: borderRadius 值
|
|
140
|
+
- [ ] **边框**: color, width
|
|
141
|
+
- [ ] **内边距**: padding (top, right, bottom, left)
|
|
142
|
+
- [ ] **元素坐标**: 每个子元素的 x, y 位置
|
|
143
|
+
- [ ] **间距**: SizedBox 的精确数值
|
|
144
|
+
- [ ] **字体**: fontSize, fontWeight, color
|
|
145
|
+
- [ ] **图标**: size, color
|
|
146
|
+
|
|
147
|
+
### 计算间距的方法
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
元素B的左边距 = 元素B的x坐标 - 元素A的x坐标 - 元素A的宽度
|
|
151
|
+
|
|
152
|
+
示例:
|
|
153
|
+
国旗x=12, width=20
|
|
154
|
+
区号x=40
|
|
155
|
+
间距 = 40 - 12 - 20 = 8px
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 💡 预防措施
|
|
161
|
+
|
|
162
|
+
### 1. 不要依赖自动布局
|
|
163
|
+
|
|
164
|
+
```dart
|
|
165
|
+
// ❌ 避免
|
|
166
|
+
mainAxisAlignment: MainAxisAlignment.spaceBetween
|
|
167
|
+
mainAxisAlignment: MainAxisAlignment.spaceEvenly
|
|
168
|
+
Expanded(flex: 1)
|
|
169
|
+
|
|
170
|
+
// ✅ 推荐
|
|
171
|
+
Container(width: 精确值)
|
|
172
|
+
SizedBox(width: 精确值)
|
|
173
|
+
Positioned(left: 精确值, top: 精确值)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 2. 使用 Sketch MCP 工具
|
|
177
|
+
|
|
178
|
+
在 Copilot Chat 中直接查询设计稿:
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
@copilot 从 Sketch 获取"国家代码选择器"的布局坐标
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 3. 创建布局配置类
|
|
185
|
+
|
|
186
|
+
```dart
|
|
187
|
+
class LoginInputConfig {
|
|
188
|
+
// 国家代码选择器
|
|
189
|
+
static const countryWidth = 98.0;
|
|
190
|
+
static const flagX = 12.0;
|
|
191
|
+
static const codeX = 40.0;
|
|
192
|
+
static const arrowX = 76.0;
|
|
193
|
+
static const dividerX = 98.0;
|
|
194
|
+
|
|
195
|
+
// 输入框
|
|
196
|
+
static const inputHeight = 54.0;
|
|
197
|
+
static const borderRadius = 16.0;
|
|
198
|
+
static const borderWidth = 1.0;
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 🔗 相关案例
|
|
205
|
+
|
|
206
|
+
- [shadow-透出问题](./shadow-透出问题.md) - 新拟态容器的完整实现
|
|
207
|
+
- [input-字段缺失](./input-字段缺失.md) - 自定义输入组件配置
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
**来源**: my_flutter 项目实战经验
|
|
212
|
+
**创建日期**: 2025-12-31
|
|
213
|
+
**最后验证**: 2026-01-16
|
|
214
|
+
**节省时间**: 4-7 轮对话
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Flutter 阴影透出问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `shadow`, `neumorphism`, `transparency`, `boxshadow`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(视觉问题)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 自动检测特征
|
|
12
|
+
|
|
13
|
+
```dart
|
|
14
|
+
// 代码模式匹配
|
|
15
|
+
BoxShadow(
|
|
16
|
+
color: Color(0x??000000), // 包含黑色阴影
|
|
17
|
+
// ...
|
|
18
|
+
)
|
|
19
|
+
// + 半透明背景
|
|
20
|
+
color: Color(0x??FFFFFF)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 用户描述关键词
|
|
24
|
+
- "阴影透出来了"
|
|
25
|
+
- "容器比设计稿暗"
|
|
26
|
+
- "失去通透感"
|
|
27
|
+
- "新拟态效果不对"
|
|
28
|
+
- "毛玻璃效果变灰"
|
|
29
|
+
|
|
30
|
+
### 问题特征
|
|
31
|
+
- [ ] 半透明容器颜色比设计稿更暗
|
|
32
|
+
- [ ] 容器有 BoxShadow 阴影
|
|
33
|
+
- [ ] 阴影包含深色(黑色/灰色)
|
|
34
|
+
- [ ] 视觉上失去"玻璃感"或"通透感"
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
39
|
+
|
|
40
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
41
|
+
|----------|-----------|---------|
|
|
42
|
+
| 降低容器透明度 | 治标不治本,破坏设计稿规格 | 1-2 轮对话 |
|
|
43
|
+
| 修改全局主题 fillColor | 问题不在主题层 | 2-3 轮对话 |
|
|
44
|
+
| 调整阴影颜色/大小 | 只要阴影在下方就会透出 | 2-3 轮对话 |
|
|
45
|
+
| 使用深色背景模拟 | Hack 方案,不同背景下失效 | 1-2 轮对话 |
|
|
46
|
+
|
|
47
|
+
**总计浪费**: 6-10 轮对话
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ✅ 正确解决方案
|
|
52
|
+
|
|
53
|
+
### 核心原理
|
|
54
|
+
|
|
55
|
+
Flutter `BoxShadow` 绘制在容器**下方**,当容器背景半透明时,阴影颜色会透过容器显示出来。
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
┌─────────────────────────┐
|
|
59
|
+
│ 阴影层(黑色) │ ← 最底层
|
|
60
|
+
├─────────────────────────┤
|
|
61
|
+
│ 容器层(55%白色) │ ← 半透明,下面的黑色透出来
|
|
62
|
+
├─────────────────────────┤
|
|
63
|
+
│ 内容层 │
|
|
64
|
+
└─────────────────────────┘
|
|
65
|
+
|
|
66
|
+
结果: 55%白色 + 黑色阴影 = 偏暗的灰色
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 解决方法:空心阴影 (Hollow Shadow)
|
|
70
|
+
|
|
71
|
+
使用 `CustomPainter` 绘制阴影,但裁剪掉中间的容器区域。
|
|
72
|
+
|
|
73
|
+
```dart
|
|
74
|
+
import 'dart:ui' as ui;
|
|
75
|
+
|
|
76
|
+
class HollowShadowPainter extends CustomPainter {
|
|
77
|
+
final List<BoxShadow> shadows;
|
|
78
|
+
final double radius;
|
|
79
|
+
|
|
80
|
+
HollowShadowPainter({required this.shadows, required this.radius});
|
|
81
|
+
|
|
82
|
+
@override
|
|
83
|
+
void paint(Canvas canvas, Size size) {
|
|
84
|
+
final RRect shape = RRect.fromRectAndRadius(
|
|
85
|
+
Rect.fromLTWH(0, 0, size.width, size.height),
|
|
86
|
+
Radius.circular(radius),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
final Path shapePath = Path()..addRRect(shape);
|
|
90
|
+
final Path canvasPath = Path()
|
|
91
|
+
..addRect(Rect.fromLTWH(-500, -500, size.width + 1000, size.height + 1000));
|
|
92
|
+
|
|
93
|
+
// 关键:挖空中间区域
|
|
94
|
+
final Path outerPath = Path.combine(
|
|
95
|
+
ui.PathOperation.difference,
|
|
96
|
+
canvasPath,
|
|
97
|
+
shapePath,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
canvas.save();
|
|
101
|
+
canvas.clipPath(outerPath); // 只在外部绘制
|
|
102
|
+
|
|
103
|
+
for (final shadow in shadows) {
|
|
104
|
+
final Paint shadowPaint = Paint()
|
|
105
|
+
..color = shadow.color
|
|
106
|
+
..maskFilter = MaskFilter.blur(
|
|
107
|
+
BlurStyle.normal,
|
|
108
|
+
shadow.blurRadius * 0.57735 + 0.5,
|
|
109
|
+
);
|
|
110
|
+
canvas.drawRRect(
|
|
111
|
+
shape.inflate(shadow.spreadRadius).shift(shadow.offset),
|
|
112
|
+
shadowPaint,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
canvas.restore();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@override
|
|
120
|
+
bool shouldRepaint(covariant HollowShadowPainter oldDelegate) {
|
|
121
|
+
return oldDelegate.shadows != shadows || oldDelegate.radius != radius;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 使用示例
|
|
127
|
+
|
|
128
|
+
```dart
|
|
129
|
+
Widget _buildNeumorphicContainer({required Widget child, double height = 54}) {
|
|
130
|
+
return CustomPaint(
|
|
131
|
+
painter: HollowShadowPainter(
|
|
132
|
+
radius: 16,
|
|
133
|
+
shadows: const [
|
|
134
|
+
BoxShadow(color: Color(0x0D000000), blurRadius: 3, offset: Offset(0, 1)),
|
|
135
|
+
BoxShadow(color: Color(0x14000000), blurRadius: 5, offset: Offset(2, 2)),
|
|
136
|
+
BoxShadow(color: Color(0xE6FFFFFF), blurRadius: 5, offset: Offset(-2, -2)),
|
|
137
|
+
],
|
|
138
|
+
),
|
|
139
|
+
child: Container(
|
|
140
|
+
height: height,
|
|
141
|
+
decoration: BoxDecoration(
|
|
142
|
+
color: const Color(0x8CFFFFFF), // 可以放心使用设计稿透明度
|
|
143
|
+
borderRadius: BorderRadius.circular(16),
|
|
144
|
+
border: Border.all(color: const Color(0x99FFFFFF), width: 1),
|
|
145
|
+
),
|
|
146
|
+
child: child,
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 📋 适用场景
|
|
155
|
+
|
|
156
|
+
- ✅ 新拟态 (Neumorphism) 设计
|
|
157
|
+
- ✅ 毛玻璃/磨砂玻璃效果
|
|
158
|
+
- ✅ 任何需要半透明背景 + 阴影的场景
|
|
159
|
+
- ✅ iOS 风格 UI
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 🔗 相关案例
|
|
164
|
+
|
|
165
|
+
- [layout-尺寸不匹配.md](./layout-尺寸不匹配.md) - 新拟态容器尺寸调整
|
|
166
|
+
- [input-字段缺失.md](./input-字段缺失.md) - CustomPainter 配合输入框使用
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
**来源**: my_flutter 项目实战经验
|
|
171
|
+
**创建日期**: 2025-12-31
|
|
172
|
+
**最后验证**: 2026-01-16
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Sketch 图标尺寸提取问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `sketch`, `icon`, `size`, `group`, `shape`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(还原精度)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "图标尺寸与设计稿不符"
|
|
13
|
+
- "设置了尺寸但没效果"
|
|
14
|
+
- "Group 和 Shape 尺寸不一样"
|
|
15
|
+
- "提取的尺寸是外层容器的"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 从 Sketch 提取了图标尺寸
|
|
19
|
+
- [ ] 实际显示比预期大或小
|
|
20
|
+
- [ ] Sketch 中图标有 Group 包裹
|
|
21
|
+
- [ ] Group 尺寸与内部 Shape 尺寸不同
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:Sketch 中图标通常有两层结构,外层 Group 用于布局定位,内层 Shape 才是实际视觉尺寸。
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Sketch 图标结构:
|
|
31
|
+
├─ Group "Tab Icon" (22x22) ← 外层容器,用于布局
|
|
32
|
+
│ └─ Shape "Icon_path" (14.67x14.67) ← 内部实际图形
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
如果提取外层 Group 的尺寸,图标会比设计稿看起来大。
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ✅ 正确解决方案
|
|
40
|
+
|
|
41
|
+
### 使用脚本提取内部 Shape 尺寸
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// Sketch 脚本:提取图标的内部实际尺寸
|
|
45
|
+
const sketch = require('sketch');
|
|
46
|
+
const document = sketch.getSelectedDocument();
|
|
47
|
+
const page = document.selectedPage;
|
|
48
|
+
|
|
49
|
+
function extractIconInternalSize(frameName) {
|
|
50
|
+
const frame = sketch.find(`[name="${frameName}"]`, page)[0];
|
|
51
|
+
if (!frame) return console.log('Frame not found');
|
|
52
|
+
|
|
53
|
+
function traverse(layer, parentY = 0) {
|
|
54
|
+
const f = layer.frame;
|
|
55
|
+
const absY = parentY + f.y;
|
|
56
|
+
|
|
57
|
+
if (layer.type === 'Group' && layer.name.includes('Icon')) {
|
|
58
|
+
console.log(`\n📦 ${layer.name} (外层 Group): ${f.width}x${f.height}`);
|
|
59
|
+
layer.layers?.forEach(child => {
|
|
60
|
+
if (child.type === 'Shape' || child.type === 'ShapePath') {
|
|
61
|
+
const cf = child.frame;
|
|
62
|
+
console.log(` └─ ${child.name} (内部图形): ${cf.width.toFixed(1)}x${cf.height.toFixed(1)} ← 使用这个`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (layer.layers) layer.layers.forEach(child => traverse(child, absY));
|
|
67
|
+
}
|
|
68
|
+
traverse(frame);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
extractIconInternalSize('Frame Name');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 输出示例
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
📦 Tab Recipient Icon (外层 Group): 22x22
|
|
78
|
+
└─ Tab Recipient Icon_path_0 (内部图形): 14.7x14.7 ← 使用这个尺寸
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Flutter 中使用正确尺寸
|
|
82
|
+
|
|
83
|
+
```dart
|
|
84
|
+
// ✅ 使用内部图形的实际尺寸
|
|
85
|
+
SvgPicture.asset(
|
|
86
|
+
'assets/icons/recipient.svg',
|
|
87
|
+
width: 15.0, // 14.7 取整
|
|
88
|
+
height: 15.0,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// ❌ 错误:使用外层 Group 尺寸
|
|
92
|
+
SvgPicture.asset(
|
|
93
|
+
'assets/icons/recipient.svg',
|
|
94
|
+
width: 22.0, // 这是 Group 尺寸,图标会偏大
|
|
95
|
+
height: 22.0,
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📝 验证检查清单
|
|
102
|
+
|
|
103
|
+
1. **确认提取的是 Shape 尺寸**
|
|
104
|
+
- 不是 Group、Frame、Artboard 的尺寸
|
|
105
|
+
- 查看脚本输出中带 ← 标记的行
|
|
106
|
+
|
|
107
|
+
2. **视觉对比验证**
|
|
108
|
+
- 在模拟器/设备上与设计稿截图对比
|
|
109
|
+
- 图标边缘应与设计稿完全重合
|
|
110
|
+
|
|
111
|
+
3. **属性使用验证**
|
|
112
|
+
```bash
|
|
113
|
+
# 搜索确认尺寸属性被使用
|
|
114
|
+
grep -n "iconSize" lib/widgets/my_widget.dart
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
120
|
+
|
|
121
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
122
|
+
|----------|-----------|---------|
|
|
123
|
+
| 调整 Flutter 中的尺寸 | 如果基准就错了,调来调去 | 3-5 轮对话 |
|
|
124
|
+
| 添加 transform scale | Hack 方案,难以精确 | 2-3 轮对话 |
|
|
125
|
+
| 重新导出 SVG | 如果不知道问题在哪,重复错误 | 2-3 轮对话 |
|
|
126
|
+
|
|
127
|
+
**最佳做法**:先用脚本确认正确尺寸,再修改代码
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📁 适用场景
|
|
132
|
+
|
|
133
|
+
- ✅ TabBar 图标
|
|
134
|
+
- ✅ 按钮内图标
|
|
135
|
+
- ✅ 任何从 Sketch 导出的图标
|