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,269 @@
|
|
|
1
|
+
# 通用组件提取与文本自适应对齐
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `component`, `refactor`, `text-align`, `adaptive`, `center`, `left`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(影响代码可维护性和 UI 一致性)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "这两个卡片样式很像"
|
|
13
|
+
- "单行居中、多行左对齐"
|
|
14
|
+
- "重复代码太多"
|
|
15
|
+
- "不同状态显示不同样式"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 多个相似组件(如 Warning Card、Error Card)硬编码实现
|
|
19
|
+
- [ ] 相同的布局结构重复多次
|
|
20
|
+
- [ ] 文本对齐方式需要根据内容长度动态调整
|
|
21
|
+
- [ ] 颜色/图标等仅通过状态切换
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
### 问题 1:硬编码重复组件
|
|
28
|
+
|
|
29
|
+
**错误做法**:为每种状态创建独立的 Widget 方法
|
|
30
|
+
|
|
31
|
+
```dart
|
|
32
|
+
// ❌ 硬编码 - 重复代码
|
|
33
|
+
Widget _buildWarningCard() { ... }
|
|
34
|
+
Widget _buildErrorCard() { ... }
|
|
35
|
+
Widget _buildSuccessCard() { ... }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**正确做法**:提取通用组件,通过参数配置样式
|
|
39
|
+
|
|
40
|
+
```dart
|
|
41
|
+
// ✅ 通用组件
|
|
42
|
+
TipCard(
|
|
43
|
+
type: TipCardType.warning, // 或 error, success, info
|
|
44
|
+
title: '标题',
|
|
45
|
+
content: '内容',
|
|
46
|
+
buttonText: '按钮',
|
|
47
|
+
onButtonPressed: () {},
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 问题 2:文本单行居中、多行左对齐
|
|
52
|
+
|
|
53
|
+
**需求**:
|
|
54
|
+
- 短文本(单行):水平居中显示
|
|
55
|
+
- 长文本(多行):左对齐显示
|
|
56
|
+
|
|
57
|
+
**解决方案**:使用 `LayoutBuilder` + `TextPainter` 动态计算
|
|
58
|
+
|
|
59
|
+
```dart
|
|
60
|
+
Widget _buildAdaptiveText(String content, TextStyle style) {
|
|
61
|
+
return LayoutBuilder(
|
|
62
|
+
builder: (context, constraints) {
|
|
63
|
+
// 计算文本是否超出一行
|
|
64
|
+
final textPainter = TextPainter(
|
|
65
|
+
text: TextSpan(text: content, style: style),
|
|
66
|
+
maxLines: 1,
|
|
67
|
+
textDirection: TextDirection.ltr,
|
|
68
|
+
);
|
|
69
|
+
textPainter.layout(maxWidth: constraints.maxWidth);
|
|
70
|
+
|
|
71
|
+
// 判断是否溢出
|
|
72
|
+
final isMultiLine = textPainter.didExceedMaxLines ||
|
|
73
|
+
textPainter.width >= constraints.maxWidth * 0.95;
|
|
74
|
+
|
|
75
|
+
return Container(
|
|
76
|
+
width: double.infinity,
|
|
77
|
+
alignment: isMultiLine ? Alignment.centerLeft : Alignment.center,
|
|
78
|
+
child: Text(
|
|
79
|
+
content,
|
|
80
|
+
style: style,
|
|
81
|
+
textAlign: isMultiLine ? TextAlign.left : TextAlign.center,
|
|
82
|
+
maxLines: 2,
|
|
83
|
+
overflow: TextOverflow.ellipsis,
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## ✅ 完整解决方案
|
|
94
|
+
|
|
95
|
+
### 1. 定义配置类
|
|
96
|
+
|
|
97
|
+
```dart
|
|
98
|
+
/// TipCard 类型枚举
|
|
99
|
+
enum TipCardType { warning, error, success, info }
|
|
100
|
+
|
|
101
|
+
/// 配置类 - 所有颜色来自 Sketch
|
|
102
|
+
class TipCardConfig {
|
|
103
|
+
final Color backgroundColor;
|
|
104
|
+
final Color borderColor;
|
|
105
|
+
final Color titleColor;
|
|
106
|
+
final Color contentColor;
|
|
107
|
+
final Color buttonShadowColor;
|
|
108
|
+
final String iconPath;
|
|
109
|
+
|
|
110
|
+
const TipCardConfig({...});
|
|
111
|
+
|
|
112
|
+
/// Warning: #fff8e7ff, border #ffe4b5ff
|
|
113
|
+
static const warning = TipCardConfig(
|
|
114
|
+
backgroundColor: Color(0xFFFFF8E7),
|
|
115
|
+
borderColor: Color(0xFFFFE4B5),
|
|
116
|
+
titleColor: Color(0xFFFF9800),
|
|
117
|
+
contentColor: Color(0xFFB45309),
|
|
118
|
+
buttonShadowColor: Color(0x40FF9800),
|
|
119
|
+
iconPath: 'assets/icons/warning_icon.svg',
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
/// Error: #f4433614 (8%透明), border #ffcacaff
|
|
123
|
+
static const error = TipCardConfig(
|
|
124
|
+
backgroundColor: Color(0x14F44336),
|
|
125
|
+
borderColor: Color(0xFFFFCACA),
|
|
126
|
+
titleColor: Color(0xFFF44336),
|
|
127
|
+
contentColor: Color(0xFFCA4F57),
|
|
128
|
+
buttonShadowColor: Color(0x40D0121B),
|
|
129
|
+
iconPath: 'assets/icons/error_icon.svg',
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
static TipCardConfig fromType(TipCardType type) {
|
|
133
|
+
switch (type) {
|
|
134
|
+
case TipCardType.warning: return warning;
|
|
135
|
+
case TipCardType.error: return error;
|
|
136
|
+
// ...
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 2. 创建通用组件
|
|
143
|
+
|
|
144
|
+
```dart
|
|
145
|
+
class TipCard extends StatelessWidget {
|
|
146
|
+
final TipCardType type;
|
|
147
|
+
final String title;
|
|
148
|
+
final String content;
|
|
149
|
+
final String buttonText;
|
|
150
|
+
final VoidCallback? onButtonPressed;
|
|
151
|
+
|
|
152
|
+
const TipCard({
|
|
153
|
+
super.key,
|
|
154
|
+
required this.type,
|
|
155
|
+
required this.title,
|
|
156
|
+
required this.content,
|
|
157
|
+
required this.buttonText,
|
|
158
|
+
this.onButtonPressed,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
@override
|
|
162
|
+
Widget build(BuildContext context) {
|
|
163
|
+
final config = TipCardConfig.fromType(type);
|
|
164
|
+
|
|
165
|
+
return Container(
|
|
166
|
+
padding: const EdgeInsets.fromLTRB(23, 17, 23, 10),
|
|
167
|
+
decoration: BoxDecoration(
|
|
168
|
+
color: config.backgroundColor,
|
|
169
|
+
borderRadius: BorderRadius.circular(16),
|
|
170
|
+
border: Border.all(color: config.borderColor, width: 1),
|
|
171
|
+
),
|
|
172
|
+
child: Column(
|
|
173
|
+
children: [
|
|
174
|
+
_buildTitleRow(config),
|
|
175
|
+
const SizedBox(height: 8),
|
|
176
|
+
_buildAdaptiveContent(config), // 自适应对齐
|
|
177
|
+
const SizedBox(height: 10),
|
|
178
|
+
_buildButton(config),
|
|
179
|
+
],
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3. 使用组件
|
|
187
|
+
|
|
188
|
+
```dart
|
|
189
|
+
// 替换硬编码实现
|
|
190
|
+
Widget _buildWarningCard(ProfileController controller) {
|
|
191
|
+
return Obx(() {
|
|
192
|
+
final status = controller.kycStatus.value;
|
|
193
|
+
|
|
194
|
+
if (status == KycStatus.expiring) {
|
|
195
|
+
return TipCard(
|
|
196
|
+
type: TipCardType.warning,
|
|
197
|
+
title: '证件即将过期',
|
|
198
|
+
content: '您的身份证件将于30天后过期,请及时更新。',
|
|
199
|
+
buttonText: '立即更新',
|
|
200
|
+
onButtonPressed: controller.updateIdDocument,
|
|
201
|
+
);
|
|
202
|
+
} else if (status == KycStatus.rejected) {
|
|
203
|
+
return TipCard(
|
|
204
|
+
type: TipCardType.error,
|
|
205
|
+
title: '验证失败',
|
|
206
|
+
content: '证件照片不清晰,请重新上传。', // 短文本会居中
|
|
207
|
+
buttonText: '重新验证',
|
|
208
|
+
onButtonPressed: controller.resubmitVerification,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return const SizedBox.shrink();
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 🔑 关键公式
|
|
220
|
+
|
|
221
|
+
### TextPainter 溢出检测
|
|
222
|
+
|
|
223
|
+
```dart
|
|
224
|
+
final isMultiLine = textPainter.didExceedMaxLines ||
|
|
225
|
+
textPainter.width >= constraints.maxWidth * 0.95;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
- `didExceedMaxLines`:文本是否超出 maxLines
|
|
229
|
+
- `width >= maxWidth * 0.95`:文本宽度接近容器宽度(留 5% 余量)
|
|
230
|
+
|
|
231
|
+
### 配置类设计模式
|
|
232
|
+
|
|
233
|
+
```dart
|
|
234
|
+
// 枚举 + 静态配置 + fromType 工厂方法
|
|
235
|
+
enum Type { a, b, c }
|
|
236
|
+
|
|
237
|
+
class Config {
|
|
238
|
+
static const a = Config(...);
|
|
239
|
+
static const b = Config(...);
|
|
240
|
+
|
|
241
|
+
static Config fromType(Type type) => switch (type) {
|
|
242
|
+
Type.a => a,
|
|
243
|
+
Type.b => b,
|
|
244
|
+
// ...
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## ⚠️ 预防措施
|
|
252
|
+
|
|
253
|
+
1. **识别相似组件**:当发现 2+ 个组件结构相同、仅样式不同时,立即提取
|
|
254
|
+
2. **颜色来自设计稿**:使用 Sketch MCP 工具获取精确颜色值
|
|
255
|
+
3. **文本对齐需求确认**:明确单行/多行的对齐规则
|
|
256
|
+
4. **配置集中管理**:将所有变体配置放在一个类中
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 🔗 相关问题
|
|
261
|
+
|
|
262
|
+
- [sketch-背景层高度.md](./sketch-背景层高度.md) - 获取精确尺寸
|
|
263
|
+
- [sketch-完整提取.md](./sketch-完整提取.md) - 一次性提取样式
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
**创建日期**: 2026-01-20
|
|
268
|
+
**最后更新**: 2026-01-20
|
|
269
|
+
**来源**: Profile 页面 TipCard 组件重构实战
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Flutter 组件字段缺失问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `component`, `props`, `missing-field`, `custom-widget`
|
|
4
|
+
> **问题类型**: 组件配置
|
|
5
|
+
> **框架**: Flutter
|
|
6
|
+
> **严重程度**: 低(编译错误,容易发现)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🔍 问题识别
|
|
11
|
+
|
|
12
|
+
### 自动检测特征
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
// 编译错误信息
|
|
16
|
+
The named parameter 'showBorder' isn't defined
|
|
17
|
+
The named parameter 'shadow' isn't defined
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 用户描述关键词
|
|
21
|
+
- "组件不支持这个属性"
|
|
22
|
+
- "字段未定义"
|
|
23
|
+
- "参数不存在"
|
|
24
|
+
- "自定义组件缺少配置"
|
|
25
|
+
|
|
26
|
+
### 问题特征
|
|
27
|
+
- [ ] 编译错误:`The named parameter 'xxx' isn't defined`
|
|
28
|
+
- [ ] 自定义组件无法接收某些属性
|
|
29
|
+
- [ ] IDE 提示参数不存在
|
|
30
|
+
- [ ] 想配置组件样式但没有对应的属性
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
35
|
+
|
|
36
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
37
|
+
|----------|-----------|---------|
|
|
38
|
+
| 直接在使用处添加样式 | 破坏组件封装性 | 1 轮对话 |
|
|
39
|
+
| 修改组件内部硬编码 | 失去可配置性 | 1-2 轮对话 |
|
|
40
|
+
| 复制组件代码到页面 | 违反 DRY 原则 | 1 轮对话 |
|
|
41
|
+
|
|
42
|
+
**总计浪费**: 2-4 轮对话
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ✅ 正确解决方案
|
|
47
|
+
|
|
48
|
+
### 核心原理
|
|
49
|
+
|
|
50
|
+
**问题根源**:自定义组件(如 `FlexInput`)在设计时未预留足够的可配置字段。
|
|
51
|
+
|
|
52
|
+
### 解决步骤
|
|
53
|
+
|
|
54
|
+
#### 1. 检查组件定义
|
|
55
|
+
|
|
56
|
+
找到组件源文件(如 `lib/core/themes/flex_widgets.dart`)
|
|
57
|
+
|
|
58
|
+
#### 2. 补充缺失字段
|
|
59
|
+
|
|
60
|
+
```dart
|
|
61
|
+
class FlexInput extends StatelessWidget {
|
|
62
|
+
const FlexInput({
|
|
63
|
+
super.key,
|
|
64
|
+
// ... 已有字段
|
|
65
|
+
this.controller,
|
|
66
|
+
this.hintText,
|
|
67
|
+
this.inputType = TextInputType.text,
|
|
68
|
+
|
|
69
|
+
// ← 新增字段
|
|
70
|
+
this.showBorder = true,
|
|
71
|
+
this.shadow,
|
|
72
|
+
this.borderColor,
|
|
73
|
+
this.borderWidth = 1.0,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// 已有属性
|
|
77
|
+
final TextEditingController? controller;
|
|
78
|
+
final String? hintText;
|
|
79
|
+
final TextInputType inputType;
|
|
80
|
+
|
|
81
|
+
// ← 新增属性
|
|
82
|
+
final bool showBorder;
|
|
83
|
+
final List<BoxShadow>? shadow;
|
|
84
|
+
final Color? borderColor;
|
|
85
|
+
final double borderWidth;
|
|
86
|
+
|
|
87
|
+
@override
|
|
88
|
+
Widget build(BuildContext context) {
|
|
89
|
+
return Container(
|
|
90
|
+
decoration: BoxDecoration(
|
|
91
|
+
// ← 使用新增的属性
|
|
92
|
+
border: showBorder
|
|
93
|
+
? Border.all(
|
|
94
|
+
color: borderColor ?? $c.border,
|
|
95
|
+
width: borderWidth,
|
|
96
|
+
)
|
|
97
|
+
: null,
|
|
98
|
+
boxShadow: shadow,
|
|
99
|
+
// ... 其他样式
|
|
100
|
+
),
|
|
101
|
+
child: TextField(
|
|
102
|
+
controller: controller,
|
|
103
|
+
decoration: InputDecoration(
|
|
104
|
+
hintText: hintText,
|
|
105
|
+
// ...
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### 3. 使用新字段
|
|
114
|
+
|
|
115
|
+
```dart
|
|
116
|
+
// 现在可以配置了
|
|
117
|
+
FlexInput(
|
|
118
|
+
hintText: 'Enter phone',
|
|
119
|
+
showBorder: false, // ← 新增
|
|
120
|
+
shadow: [ // ← 新增
|
|
121
|
+
BoxShadow(
|
|
122
|
+
color: Color(0x0D000000),
|
|
123
|
+
blurRadius: 3,
|
|
124
|
+
offset: Offset(0, 1),
|
|
125
|
+
),
|
|
126
|
+
],
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 📋 组件设计最佳实践
|
|
133
|
+
|
|
134
|
+
### 必须预留的常用字段
|
|
135
|
+
|
|
136
|
+
#### 样式相关
|
|
137
|
+
```dart
|
|
138
|
+
// 边框
|
|
139
|
+
final bool showBorder;
|
|
140
|
+
final Color? borderColor;
|
|
141
|
+
final double borderWidth;
|
|
142
|
+
final BorderRadius? borderRadius;
|
|
143
|
+
|
|
144
|
+
// 阴影
|
|
145
|
+
final List<BoxShadow>? shadow;
|
|
146
|
+
|
|
147
|
+
// 背景
|
|
148
|
+
final Color? backgroundColor;
|
|
149
|
+
final Gradient? gradient;
|
|
150
|
+
|
|
151
|
+
// 尺寸
|
|
152
|
+
final double? width;
|
|
153
|
+
final double? height;
|
|
154
|
+
final EdgeInsets? padding;
|
|
155
|
+
final EdgeInsets? margin;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### 交互相关
|
|
159
|
+
```dart
|
|
160
|
+
// 状态
|
|
161
|
+
final bool enabled;
|
|
162
|
+
final bool readOnly;
|
|
163
|
+
|
|
164
|
+
// 回调
|
|
165
|
+
final VoidCallback? onTap;
|
|
166
|
+
final ValueChanged<String>? onChanged;
|
|
167
|
+
final VoidCallback? onSubmitted;
|
|
168
|
+
|
|
169
|
+
// 验证
|
|
170
|
+
final String? Function(String?)? validator;
|
|
171
|
+
final bool showError;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 组件设计检查清单
|
|
175
|
+
|
|
176
|
+
- [ ] 是否支持自定义颜色?
|
|
177
|
+
- [ ] 是否支持自定义尺寸?
|
|
178
|
+
- [ ] 是否支持显示/隐藏边框?
|
|
179
|
+
- [ ] 是否支持阴影配置?
|
|
180
|
+
- [ ] 是否支持禁用/只读状态?
|
|
181
|
+
- [ ] 回调函数是否足够?
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 💡 预防措施
|
|
186
|
+
|
|
187
|
+
### 1. 使用 Token 系统
|
|
188
|
+
|
|
189
|
+
```dart
|
|
190
|
+
// ✅ 推荐:使用 Token 作为默认值
|
|
191
|
+
final Color borderColor;
|
|
192
|
+
|
|
193
|
+
FlexInput({
|
|
194
|
+
this.borderColor = $c.border, // Token 默认值
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// ❌ 避免:硬编码
|
|
198
|
+
final Color borderColor = Color(0xFFDDDDDD);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 2. 参考 Material/Cupertino 组件
|
|
202
|
+
|
|
203
|
+
Flutter 官方组件的属性设计很完善,可以参考:
|
|
204
|
+
|
|
205
|
+
```dart
|
|
206
|
+
// TextField 的部分属性
|
|
207
|
+
TextField(
|
|
208
|
+
decoration: InputDecoration(
|
|
209
|
+
border: ...,
|
|
210
|
+
enabledBorder: ...,
|
|
211
|
+
focusedBorder: ...,
|
|
212
|
+
filled: ...,
|
|
213
|
+
fillColor: ...,
|
|
214
|
+
),
|
|
215
|
+
style: ...,
|
|
216
|
+
enabled: ...,
|
|
217
|
+
readOnly: ...,
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 3. 渐进式添加属性
|
|
222
|
+
|
|
223
|
+
**不要一开始就添加所有属性**,遵循 YAGNI 原则:
|
|
224
|
+
1. 先实现基础功能
|
|
225
|
+
2. 使用时发现缺少属性
|
|
226
|
+
3. 及时补充属性
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 🔗 相关案例
|
|
231
|
+
|
|
232
|
+
- [layout-尺寸不匹配](./layout-尺寸不匹配.md) - 组件的精确尺寸配置
|
|
233
|
+
- [shadow-透出问题](./shadow-透出问题.md) - CustomPainter 组件的属性设计
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
**来源**: my_flutter 项目实战经验
|
|
238
|
+
**创建日期**: 2025-12-31
|
|
239
|
+
**最后验证**: 2026-01-16
|
|
240
|
+
**节省时间**: 2-4 轮对话
|