mta-mcp 1.0.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/README.md +818 -0
- package/agents/_TEMPLATE.md +153 -0
- package/agents/flutter.agent.md +222 -0
- package/agents/i18n.agent.md +78 -0
- package/agents/logicflow.agent.md +97 -0
- package/agents/vue3.agent.md +176 -0
- package/agents/wechat-miniprogram.agent.md +89 -0
- package/bin/mta.cjs +132 -0
- package/common/i18n.md +385 -0
- package/common/typescript-strict.md +186 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +6493 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
- package/standards/README.md +194 -0
- package/standards/core/code-generation.md +421 -0
- package/standards/core/code-style.md +308 -0
- package/standards/core/dart-base.md +572 -0
- package/standards/core/mandatory-rules.md +103 -0
- package/standards/core/typescript-base.md +179 -0
- package/standards/frameworks/flutter-ui-system.md +497 -0
- package/standards/frameworks/flutter.md +1268 -0
- package/standards/frameworks/pinia.md +172 -0
- package/standards/frameworks/vue3-composition.md +779 -0
- package/standards/frameworks/wechat-miniprogram.md +2177 -0
- package/standards/libraries/element-plus.md +1128 -0
- package/standards/libraries/i18n.md +360 -0
- package/standards/libraries/logicflow.md +1007 -0
- package/standards/patterns/api-layer.md +187 -0
- package/standards/patterns/component-design.md +200 -0
- package/standards/patterns/design-system-restoration.md +570 -0
- package/standards/patterns/vue-api-mock-layer.md +958 -0
- package/standards/patterns/vue-css-nesting.md +604 -0
- package/standards/troubleshooting-cases/flutter/textfield-vertical-centering.md +107 -0
- package/standards/workflows/design-restoration-guide.md +164 -0
- package/standards/workflows/large-project-split.md +359 -0
- package/standards/workflows/problem-diagnosis.md +280 -0
- package/standards/workflows/textfield-centering-guide.md +157 -0
- package/templates/README.md +144 -0
- package/templates/common/types/_CONFIG.md +12 -0
- package/templates/common/types/api.ts +39 -0
- package/templates/common/types/common.ts +70 -0
- package/templates/config-templates/agents-section.md +9 -0
- package/templates/config-templates/custom-section.md +6 -0
- package/templates/config-templates/header.md +29 -0
- package/templates/config-templates/workflow-minimal.md +44 -0
- package/templates/copilot-instructions-mcp-optimized.md +158 -0
- package/templates/vue/api-layer/_CONFIG.md +145 -0
- package/templates/vue/api-layer/index.ts +58 -0
- package/templates/vue/api-layer/mock/index.ts +122 -0
- package/templates/vue/api-layer/modules/_template.ts +109 -0
- package/templates/vue/api-layer/modules/index.ts +16 -0
- package/templates/vue/api-layer/request.ts +279 -0
- package/templates/vue/api-layer/types.ts +80 -0
- package/troubleshooting/README.md +368 -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/component-/351/200/232/347/224/250/345/214/226/346/217/220/345/217/226.md +269 -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/210/227/350/241/250item/345/214/272/345/237/237.md +212 -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/sketch-/350/203/214/346/231/257/345/261/202/351/253/230/345/272/246.md +264 -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,264 @@
|
|
|
1
|
+
# Sketch Frame 与 Background 层高度差异问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `sketch`, `frame`, `background`, `height`, `_background`, `_bg`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 高(导致布局错位和多轮返工)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "间距不对"
|
|
13
|
+
- "高度算错了"
|
|
14
|
+
- "内边距有问题"
|
|
15
|
+
- "最后一个 item 高度不对"
|
|
16
|
+
- "卡片高度不匹配"
|
|
17
|
+
|
|
18
|
+
### 问题特征
|
|
19
|
+
- [ ] 计算的间距与设计稿视觉效果不符
|
|
20
|
+
- [ ] 使用 Frame 高度计算,但结果总是差几像素
|
|
21
|
+
- [ ] Menu/List 的第一个或最后一个 item 高度异常
|
|
22
|
+
- [ ] 多个卡片之间的 gap 计算错误
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🎯 核心原理
|
|
27
|
+
|
|
28
|
+
**根本原因**:Sketch 的 Frame(组/画板)高度与其内部 `_background` 或 `_bg` 层高度可能不同。
|
|
29
|
+
|
|
30
|
+
### Sketch 层级结构示例
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Menu Card (Frame) ← height: 336px ❌ 错误值
|
|
34
|
+
├── Menu Card_background ← height: 350px ✅ 正确值
|
|
35
|
+
├── Menu Item 1
|
|
36
|
+
├── Divider 1
|
|
37
|
+
├── Menu Item 2
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
Warning Card (Frame) ← height: 100px ❌ 错误值
|
|
41
|
+
├── Warning Card_bg ← height: 127px ✅ 正确值
|
|
42
|
+
├── Warning Text
|
|
43
|
+
├── Button
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| 元素 | Frame 高度 | _background 高度 | 差异 |
|
|
47
|
+
|------|------------|------------------|------|
|
|
48
|
+
| Menu Card | 336px | 350px | +14px |
|
|
49
|
+
| Warning Card | 100px | 127px | +27px |
|
|
50
|
+
|
|
51
|
+
### 为什么会这样?
|
|
52
|
+
|
|
53
|
+
1. **Auto Layout**: Frame 可能使用 Hug Contents 或固定高度
|
|
54
|
+
2. **阴影/装饰**: _background 层包含了内边距、阴影区域
|
|
55
|
+
3. **Padding 内含**: 背景层可能比 Frame 更大以提供视觉边距
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
60
|
+
|
|
61
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
62
|
+
|----------|-----------|---------|
|
|
63
|
+
| 使用 Frame 高度计算 gap | 背景层实际更大 | 3-5 轮对话 |
|
|
64
|
+
| 反复调整 padding | 不知道正确的参考值 | 4-6 轮对话 |
|
|
65
|
+
| 假设最后 item 与其他相同 | 底部 padding 通常不同 | 2-3 轮对话 |
|
|
66
|
+
| 只测量 divider 位置 | 忽略了背景层边界 | 2-3 轮对话 |
|
|
67
|
+
|
|
68
|
+
**总计浪费**: 10-15 轮对话
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## ✅ 正确解决方案
|
|
73
|
+
|
|
74
|
+
### 解决步骤
|
|
75
|
+
|
|
76
|
+
#### 1. 必须同时测量 Frame 和 _background 层
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
// Sketch 测量脚本 - 比较 Frame 与背景层高度
|
|
80
|
+
const sketch = require('sketch');
|
|
81
|
+
const doc = sketch.getSelectedDocument();
|
|
82
|
+
const page = doc.selectedPage;
|
|
83
|
+
const target = sketch.find('[name="Profile Page"]', page)[0]; // 替换为目标
|
|
84
|
+
|
|
85
|
+
function measureBackgroundLayers(container) {
|
|
86
|
+
const results = {};
|
|
87
|
+
|
|
88
|
+
sketch.find('*', container).forEach(layer => {
|
|
89
|
+
const name = layer.name;
|
|
90
|
+
|
|
91
|
+
// 查找 Frame 和对应的 _background/_bg 层
|
|
92
|
+
if (name.includes('Card') || name.includes('Panel')) {
|
|
93
|
+
results[name] = {
|
|
94
|
+
type: layer.type,
|
|
95
|
+
frameHeight: layer.frame.height,
|
|
96
|
+
y: layer.frame.y
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 单独记录 _background 和 _bg 层
|
|
101
|
+
if (name.includes('_background') || name.includes('_bg')) {
|
|
102
|
+
results[name] = {
|
|
103
|
+
type: layer.type,
|
|
104
|
+
height: layer.frame.height,
|
|
105
|
+
y: layer.frame.y
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
console.log(JSON.stringify(results, null, 2));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
measureBackgroundLayers(target);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### 2. 使用 _background 层高度计算布局
|
|
117
|
+
|
|
118
|
+
```dart
|
|
119
|
+
// ❌ 错误: 使用 Frame 高度
|
|
120
|
+
const warningCardHeight = 100; // Frame 高度
|
|
121
|
+
const menuCardY = 274;
|
|
122
|
+
const gap = menuCardY - (warningCardY + warningCardHeight);
|
|
123
|
+
// gap = 274 - (130 + 100) = 44px ❌
|
|
124
|
+
|
|
125
|
+
// ✅ 正确: 使用 _background 层高度
|
|
126
|
+
const warningCardHeight = 127; // _bg 层高度
|
|
127
|
+
const gap = menuCardY - (warningCardY + warningCardHeight);
|
|
128
|
+
// gap = 274 - (130 + 127) = 17px ✅
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### 3. 区分列表首尾 item 的 padding
|
|
132
|
+
|
|
133
|
+
Menu/List 类组件中,首尾 item 通常与背景边缘有特殊间距:
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
// 测量 divider 位置和背景层边界
|
|
137
|
+
const dividers = sketch.find('[name*="Divider"]', menuCard);
|
|
138
|
+
const background = sketch.find('[name*="_background"]', menuCard)[0];
|
|
139
|
+
|
|
140
|
+
dividers.forEach(d => {
|
|
141
|
+
console.log(`${d.name}: y=${d.frame.y}`);
|
|
142
|
+
});
|
|
143
|
+
console.log(`Background height: ${background.frame.height}`);
|
|
144
|
+
|
|
145
|
+
// 计算每个区域高度
|
|
146
|
+
// Item 1: 0 → divider1.y
|
|
147
|
+
// Item 2-N-1: divider[i-1].y → divider[i].y
|
|
148
|
+
// Item N: divider[N-1].y + 1 → background.height
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### 4. Flutter 实现:区分首尾 item
|
|
152
|
+
|
|
153
|
+
```dart
|
|
154
|
+
Widget _buildMenuItem(int index, MenuItem item) {
|
|
155
|
+
// Sketch 精确测量 (Menu Card_background = 350px):
|
|
156
|
+
// - Item 1: 区域63px = top:20 + icon:36 + bottom:6 + divider:1
|
|
157
|
+
// - Item 2-5: 区域56px = top:12 + icon:36 + bottom:7 + divider:1
|
|
158
|
+
// - Item 6: 区域63px = top:12 + icon:36 + bottom:15 (无divider)
|
|
159
|
+
|
|
160
|
+
return Padding(
|
|
161
|
+
padding: EdgeInsets.fromLTRB(
|
|
162
|
+
16,
|
|
163
|
+
index == 0 ? 20 : 12, // 首 item top 更大
|
|
164
|
+
16,
|
|
165
|
+
index == 0 ? 6 : (index == 5 ? 15 : 7), // 尾 item bottom 更大
|
|
166
|
+
),
|
|
167
|
+
child: Row(...),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🔑 关键公式
|
|
175
|
+
|
|
176
|
+
### 卡片间距计算
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
实际 gap = 下一卡片.y - (当前卡片.y + 当前卡片._background.height)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 列表 item 区域计算
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
区域[0] = divider[0].y // 首 item
|
|
186
|
+
区域[i] = divider[i].y - divider[i-1].y // 中间 items (含 divider)
|
|
187
|
+
区域[N] = background.height - divider[N-1].y - 1 // 尾 item (无 divider)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Icon 垂直 padding 计算
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
top_padding = icon.y - region_start
|
|
194
|
+
bottom_padding = region_end - (icon.y + icon.height) - divider_height
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## ⚠️ 预防措施
|
|
200
|
+
|
|
201
|
+
### 提取 Sketch 时的检查清单
|
|
202
|
+
|
|
203
|
+
- [ ] 是否同时读取了 Frame 高度和 _background/_bg 层高度?
|
|
204
|
+
- [ ] 两者是否相同?如果不同,使用哪个?
|
|
205
|
+
- [ ] 卡片之间的 gap 是否基于视觉边界(_background)计算?
|
|
206
|
+
- [ ] 列表的首尾 item 是否有特殊的 padding?
|
|
207
|
+
|
|
208
|
+
### 命名约定识别
|
|
209
|
+
|
|
210
|
+
| 后缀 | 含义 | 使用场景 |
|
|
211
|
+
|------|------|----------|
|
|
212
|
+
| `_background` | 完整背景层 | 用于计算实际高度 |
|
|
213
|
+
| `_bg` | 简化背景层 | 同上 |
|
|
214
|
+
| `_shadow` | 阴影层 | 可能影响视觉边界 |
|
|
215
|
+
| `_border` | 边框层 | 不影响高度计算 |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 📊 真实案例
|
|
220
|
+
|
|
221
|
+
### 案例: Profile 页面卡片间距
|
|
222
|
+
|
|
223
|
+
**问题**: Warning Card 到 Menu Card 的间距应该是 17px,但计算得到 44px
|
|
224
|
+
|
|
225
|
+
**错误分析**:
|
|
226
|
+
```
|
|
227
|
+
Menu Card.y = 274
|
|
228
|
+
Warning Card.y = 130
|
|
229
|
+
Warning Card Frame.height = 100 ← 使用了错误的值
|
|
230
|
+
|
|
231
|
+
gap = 274 - (130 + 100) = 44px ❌
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**正确分析**:
|
|
235
|
+
```
|
|
236
|
+
Menu Card.y = 274
|
|
237
|
+
Warning Card.y = 130
|
|
238
|
+
Warning Card_bg.height = 127 ← 使用 _bg 层高度
|
|
239
|
+
|
|
240
|
+
gap = 274 - (130 + 127) = 17px ✅
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**修复代码**:
|
|
244
|
+
```dart
|
|
245
|
+
// 修改前
|
|
246
|
+
SizedBox(height: 44),
|
|
247
|
+
|
|
248
|
+
// 修改后
|
|
249
|
+
SizedBox(height: 17),
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 🔗 相关问题
|
|
255
|
+
|
|
256
|
+
- [layout-尺寸不匹配.md](./layout-尺寸不匹配.md) - 通用布局问题
|
|
257
|
+
- [sketch-完整提取.md](./sketch-完整提取.md) - 完整提取脚本
|
|
258
|
+
- [sketch-图标尺寸.md](./sketch-图标尺寸.md) - SVG 尺寸问题
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
**创建日期**: 2026-01-19
|
|
263
|
+
**最后更新**: 2026-01-19
|
|
264
|
+
**来源**: Profile 页面 Menu Card 布局调试实战
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Flutter SVG 未居中问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `svg`, `viewbox`, `center`, `alignment`, `icon`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 低(布局问题)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "SVG 图标没有居中"
|
|
13
|
+
- "图标位置偏了"
|
|
14
|
+
- "需要用 Center 包裹"
|
|
15
|
+
- "viewBox 不对"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] SVG 显示位置与预期不符
|
|
19
|
+
- [ ] 需要额外的 Center/Align 组件包裹
|
|
20
|
+
- [ ] SVG viewBox 尺寸与使用尺寸不匹配
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 🎯 核心原理
|
|
25
|
+
|
|
26
|
+
**根本原因**:Sketch 导出 SVG 时,viewBox 可能是图形的最小边界框,而非设计稿中的容器尺寸。
|
|
27
|
+
|
|
28
|
+
```xml
|
|
29
|
+
<!-- ❌ 问题:viewBox 是最小边界框 -->
|
|
30
|
+
<!-- 箭头图标实际尺寸是 6x3,但需要放在 12x12 的容器中 -->
|
|
31
|
+
<svg viewBox="0 0 6 3">
|
|
32
|
+
<path d="..."/>
|
|
33
|
+
</svg>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```dart
|
|
37
|
+
// 放在 12x12 容器中,图标会被拉伸或需要额外居中处理
|
|
38
|
+
SvgPicture.asset('arrow.svg', width: 12, height: 12);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## ✅ 正确解决方案
|
|
44
|
+
|
|
45
|
+
### 方案1:导出时保留完整 viewBox(推荐)
|
|
46
|
+
|
|
47
|
+
在 Sketch 中导出 SVG 时,选择图标所在的外层容器(Group),而非内部的形状:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// Sketch 脚本:导出完整容器的 SVG
|
|
51
|
+
const sketch = require('sketch');
|
|
52
|
+
const layer = sketch.find('[name="Icon Container"]')[0]; // 选择外层 Group
|
|
53
|
+
const svg = sketch.export(layer, { formats: 'svg', output: false });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```xml
|
|
57
|
+
<!-- ✅ 正确:viewBox 包含完整容器空间 -->
|
|
58
|
+
<svg viewBox="0 0 12 12">
|
|
59
|
+
<!-- 箭头在容器中的正确位置 -->
|
|
60
|
+
<path transform="translate(3, 4.5)" d="..."/>
|
|
61
|
+
</svg>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 方案2:Flutter 中手动设置对齐
|
|
65
|
+
|
|
66
|
+
```dart
|
|
67
|
+
// 如果 SVG 无法重新导出,在 Flutter 中处理
|
|
68
|
+
SizedBox(
|
|
69
|
+
width: 12,
|
|
70
|
+
height: 12,
|
|
71
|
+
child: Center(
|
|
72
|
+
child: SvgPicture.asset(
|
|
73
|
+
'arrow.svg',
|
|
74
|
+
width: 6, // 使用 SVG 实际尺寸
|
|
75
|
+
height: 3,
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 📝 Sketch 图标导出检查清单
|
|
84
|
+
|
|
85
|
+
1. **确认选择正确的图层**
|
|
86
|
+
- 选择外层 Group/Frame,而非内部 Shape
|
|
87
|
+
- 外层应该是设计稿中图标的"点击区域"尺寸
|
|
88
|
+
|
|
89
|
+
2. **验证导出的 viewBox**
|
|
90
|
+
```bash
|
|
91
|
+
# 检查 SVG 文件的 viewBox
|
|
92
|
+
head -3 assets/icons/arrow.svg
|
|
93
|
+
# 应该看到 viewBox="0 0 12 12" 而非 viewBox="0 0 6 3"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
3. **检查 Flutter 中的使用**
|
|
97
|
+
```dart
|
|
98
|
+
// 宽高应该与 viewBox 匹配
|
|
99
|
+
SvgPicture.asset('arrow.svg', width: 12, height: 12);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
105
|
+
|
|
106
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
107
|
+
|----------|-----------|---------|
|
|
108
|
+
| 用 Center 包裹 | 治标不治本,每个图标都要处理 | 1 轮对话/图标 |
|
|
109
|
+
| 调整 alignment 属性 | SvgPicture 的 alignment 可能不生效 | 1-2 轮对话 |
|
|
110
|
+
| 修改 SVG 的 transform | 需要计算精确偏移量 | 2-3 轮对话 |
|
|
111
|
+
|
|
112
|
+
**最佳做法**:从源头修复,重新导出正确 viewBox 的 SVG
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 📁 适用场景
|
|
117
|
+
|
|
118
|
+
- ✅ 小尺寸图标(箭头、点击指示器等)
|
|
119
|
+
- ✅ 需要放在固定容器中的图标
|
|
120
|
+
- ✅ TabBar、按钮中的图标
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Flutter SVG 颜色异常问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `svg`, `color`, `colorfilter`, `transparency`, `icon`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(视觉问题)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "SVG 颜色比设计稿浅"
|
|
13
|
+
- "图标颜色不对"
|
|
14
|
+
- "透明度丢失"
|
|
15
|
+
- "图标颜色变淡了"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 使用 flutter_svg 加载 SVG 图标
|
|
19
|
+
- [ ] 使用了 ColorFilter.mode
|
|
20
|
+
- [ ] SVG 原本有透明度(如 #1C2B45B3)
|
|
21
|
+
- [ ] 实际显示颜色比设计稿浅
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:`ColorFilter.mode(color, BlendMode.srcIn)` 会完全覆盖 SVG 的原有颜色和透明度。
|
|
28
|
+
|
|
29
|
+
```dart
|
|
30
|
+
// Sketch 设计稿:#1C2B45B3(70% 透明度的深蓝色)
|
|
31
|
+
// SVG 文件中:fill="#1C2B45" fill-opacity="0.7"
|
|
32
|
+
|
|
33
|
+
// ❌ 使用 ColorFilter 后:
|
|
34
|
+
SvgPicture.asset(
|
|
35
|
+
'icon.svg',
|
|
36
|
+
colorFilter: ColorFilter.mode(
|
|
37
|
+
Color(0xFF1C2B45), // 只有颜色,没有透明度
|
|
38
|
+
BlendMode.srcIn, // 完全覆盖原有样式
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
// 结果:显示为 100% 的深蓝色,比设计稿深/浅
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ✅ 正确解决方案
|
|
47
|
+
|
|
48
|
+
### 方案1:保留 SVG 原有样式(推荐)
|
|
49
|
+
|
|
50
|
+
```dart
|
|
51
|
+
// ✅ 不使用 colorFilter,保留 SVG 原有颜色和透明度
|
|
52
|
+
SvgPicture.asset(
|
|
53
|
+
'assets/icons/dropdown_arrow.svg',
|
|
54
|
+
width: 12,
|
|
55
|
+
height: 12,
|
|
56
|
+
// 不传 colorFilter
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 方案2:需要动态颜色时,包含透明度
|
|
61
|
+
|
|
62
|
+
```dart
|
|
63
|
+
// ✅ 如果必须使用 colorFilter,颜色要包含 alpha
|
|
64
|
+
SvgPicture.asset(
|
|
65
|
+
'icon.svg',
|
|
66
|
+
colorFilter: customColor != null
|
|
67
|
+
? ColorFilter.mode(
|
|
68
|
+
customColor.withValues(alpha: 0.7), // 保留透明度
|
|
69
|
+
BlendMode.srcIn,
|
|
70
|
+
)
|
|
71
|
+
: null, // 无自定义颜色时保留原样式
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 方案3:SVG 导出时保留透明度信息
|
|
76
|
+
|
|
77
|
+
```xml
|
|
78
|
+
<!-- Sketch 导出 SVG 时确保保留 fill-opacity -->
|
|
79
|
+
<path fill="#1C2B45" fill-opacity="0.7" d="..."/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 📝 Sketch 颜色透明度转换
|
|
85
|
+
|
|
86
|
+
Sketch 颜色格式:`#RRGGBBAA`(最后两位是透明度)
|
|
87
|
+
|
|
88
|
+
| Sketch | Flutter | 说明 |
|
|
89
|
+
|--------|---------|------|
|
|
90
|
+
| #1C2B45B3 | Color(0xB31C2B45) | 70% 透明度 |
|
|
91
|
+
| #00000080 | Color(0x80000000) | 50% 透明度 |
|
|
92
|
+
| #FFFFFF26 | Color(0x26FFFFFF) | 15% 透明度 |
|
|
93
|
+
|
|
94
|
+
**常用透明度对照表**:
|
|
95
|
+
- 100% = FF
|
|
96
|
+
- 70% = B3
|
|
97
|
+
- 50% = 80
|
|
98
|
+
- 30% = 4D
|
|
99
|
+
- 15% = 26
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
104
|
+
|
|
105
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
106
|
+
|----------|-----------|---------|
|
|
107
|
+
| 调整 SVG 本身的颜色 | ColorFilter 会覆盖 | 1-2 轮对话 |
|
|
108
|
+
| 修改 BlendMode | srcIn 是正确的混合模式 | 1-2 轮对话 |
|
|
109
|
+
| 用 Opacity widget 包裹 | 影响整个图标,不是颜色透明度 | 1 轮对话 |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 📁 适用场景
|
|
114
|
+
|
|
115
|
+
- ✅ 设计稿中有透明度的图标
|
|
116
|
+
- ✅ TabBar 图标
|
|
117
|
+
- ✅ 任何使用 flutter_svg 的场景
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Flutter TabBar 动画同步问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `tabbar`, `animation`, `neumorphism`, `color`, `sync`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(视觉体验)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "颜色变化太快"
|
|
13
|
+
- "动画不同步"
|
|
14
|
+
- "背景块还没移动图标就变色了"
|
|
15
|
+
- "选中/未选中切换不流畅"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] TabBar 有背景滑块动画
|
|
19
|
+
- [ ] 图标/文字颜色需要随选中状态变化
|
|
20
|
+
- [ ] 背景块使用 AnimatedPositioned/AnimatedContainer
|
|
21
|
+
- [ ] 颜色使用 setState 直接切换
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:背景块使用了动画(如 AnimatedPositioned),但颜色切换使用 `setState` 立即更新,导致颜色变化比背景块移动快。
|
|
28
|
+
|
|
29
|
+
```dart
|
|
30
|
+
// ❌ 错误:颜色立即变化
|
|
31
|
+
setState(() {
|
|
32
|
+
selectedIndex = newIndex; // 颜色立即切换
|
|
33
|
+
});
|
|
34
|
+
// 同时 AnimatedPositioned 的背景块需要 300ms 移动
|
|
35
|
+
// 结果:图标已经变白,背景还没移到位
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## ✅ 正确解决方案
|
|
41
|
+
|
|
42
|
+
**使用 TweenAnimationBuilder 同步所有动画**
|
|
43
|
+
|
|
44
|
+
```dart
|
|
45
|
+
const animDuration = Duration(milliseconds: 300);
|
|
46
|
+
const animCurve = Curves.easeInOut;
|
|
47
|
+
|
|
48
|
+
// 背景块动画
|
|
49
|
+
AnimatedPositioned(
|
|
50
|
+
duration: animDuration,
|
|
51
|
+
curve: animCurve,
|
|
52
|
+
left: targetLeft,
|
|
53
|
+
child: greenBackground,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
// 颜色动画(同步)
|
|
57
|
+
TweenAnimationBuilder<Color?>(
|
|
58
|
+
duration: animDuration,
|
|
59
|
+
curve: animCurve,
|
|
60
|
+
tween: ColorTween(
|
|
61
|
+
begin: isSelected ? unselectedColor : selectedColor,
|
|
62
|
+
end: isSelected ? selectedColor : unselectedColor,
|
|
63
|
+
),
|
|
64
|
+
builder: (_, color, child) => Icon(
|
|
65
|
+
icon,
|
|
66
|
+
color: color,
|
|
67
|
+
size: iconSize,
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// 布局动画(同步)
|
|
72
|
+
AnimatedContainer(
|
|
73
|
+
duration: animDuration,
|
|
74
|
+
curve: animCurve,
|
|
75
|
+
height: isSelected ? 44.0 : 22.0,
|
|
76
|
+
child: icon,
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 关键要点
|
|
81
|
+
1. **统一动画参数**:所有动画使用相同的 `duration` 和 `curve`
|
|
82
|
+
2. **颜色也要动画**:使用 `TweenAnimationBuilder<Color?>` 而非直接 setState
|
|
83
|
+
3. **布局也要动画**:如果选中/未选中的布局不同,也要使用 AnimatedContainer
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
88
|
+
|
|
89
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
90
|
+
|----------|-----------|---------|
|
|
91
|
+
| 调整动画时长 | 不解决同步问题 | 1-2 轮对话 |
|
|
92
|
+
| 使用 AnimatedSwitcher | 会导致整个组件重建 | 2-3 轮对话 |
|
|
93
|
+
| 添加延迟切换颜色 | Hack 方案,难以精确匹配 | 2-3 轮对话 |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 📁 适用场景
|
|
98
|
+
|
|
99
|
+
- ✅ 底部导航栏 TabBar
|
|
100
|
+
- ✅ 分段控制器 SegmentedControl
|
|
101
|
+
- ✅ 任何需要"选中滑块 + 颜色变化"的组件
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🔗 相关案例
|
|
106
|
+
|
|
107
|
+
- [clip-阴影裁剪](./clip-阴影裁剪.md) - 滑块阴影被裁剪问题
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# withOpacity 弃用警告
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `deprecation`, `color`, `opacity`, `flutter3`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 低(警告)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### IDE 提示
|
|
12
|
+
```
|
|
13
|
+
'withOpacity' is deprecated and shouldn't be used.
|
|
14
|
+
Use withValues to avoid precision loss.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 使用了 `Color.withOpacity()`
|
|
19
|
+
- [ ] IDE 显示删除线警告
|
|
20
|
+
- [ ] Flutter 3.x 版本
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 🎯 核心原理
|
|
25
|
+
|
|
26
|
+
**原因**:Flutter 3.x 弃用了 `withOpacity()` 方法,因为浮点数精度问题。推荐使用精度更高的 `withValues()`。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ✅ 正确解决方案
|
|
31
|
+
|
|
32
|
+
### 替换语法
|
|
33
|
+
|
|
34
|
+
```dart
|
|
35
|
+
// ❌ 弃用写法
|
|
36
|
+
color: Colors.white.withOpacity(0.7)
|
|
37
|
+
color: const Color(0xFF1C2B45).withOpacity(0.15)
|
|
38
|
+
|
|
39
|
+
// ✅ 新写法
|
|
40
|
+
color: Colors.white.withValues(alpha: 0.7)
|
|
41
|
+
color: const Color(0xFF1C2B45).withValues(alpha: 0.15)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 批量替换命令
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# 查找所有使用
|
|
48
|
+
grep -r "\.withOpacity(" lib/
|
|
49
|
+
|
|
50
|
+
# macOS/Linux sed 替换
|
|
51
|
+
find lib -name "*.dart" -exec sed -i '' 's/\.withOpacity(\([^)]*\))/.withValues(alpha: \1)/g' {} \;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### IDE 全局替换
|
|
55
|
+
- 搜索:`.withOpacity(`
|
|
56
|
+
- 替换:`.withValues(alpha: `
|
|
57
|
+
- 注意:需要手动调整括号
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📝 注意事项
|
|
62
|
+
|
|
63
|
+
1. **const 表达式**:`withValues` 同样不能在 const 中使用
|
|
64
|
+
```dart
|
|
65
|
+
// 两种都不行用于 const
|
|
66
|
+
const color = Colors.white.withOpacity(0.5); // ❌
|
|
67
|
+
const color = Colors.white.withValues(alpha: 0.5); // ❌
|
|
68
|
+
|
|
69
|
+
// const 颜色需要直接指定 alpha
|
|
70
|
+
const color = Color(0x80FFFFFF); // ✅ 0x80 = 50% alpha
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
2. **alpha 范围**:0.0 - 1.0(与 withOpacity 相同)
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 📁 适用场景
|
|
78
|
+
|
|
79
|
+
- ✅ Flutter 3.x 项目升级
|
|
80
|
+
- ✅ 消除 IDE 警告
|
|
81
|
+
- ✅ 代码规范化
|