ワンクリックで
widget-size-checklist
确保小组件组件根据 HomeWidgetSize 正确调整各元素大小,包括容器、图标、字体、间距、线条粗细等
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
确保小组件组件根据 HomeWidgetSize 正确调整各元素大小,包括容器、图标、字体、间距、线条粗细等
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
迁移小组件到事件携带数据模式,优化刷新性能。将数据直接放在事件中传递,小组件直接使用事件数据,避免重复的缓存访问操作。
为插件主页添加带过滤器的列表小组件,使用 commonWidgetsProvider 支持多种公共组件样式,根据过滤条件选择使用多步骤 data selectors 或自定义表单
将插件中内置的dataRenderer(自定义UI)封装成标准的公共小组件,使其可被其他插件或功能复用。核心特性:(1)提取dataRenderer的UI逻辑创建独立组件文件,(2)在common_widgets.dart中注册新组件,(3)在_provideCommonWidgets中添加数据提供函数,(4)移除旧的内置渲染器代码
从 code_snippet 原型文件创建可预览的 Dart 小组件。自动分析 HTML 设计、生成 Flutter 组件、注册路由并添加到组件展示列表。
为Flutter插件添加可配置的选择器小组件(HomeWidget),支持用户点击配置、数据选择和动态数据渲染。核心特性:(1) 配置dataSelector保存必要数据,(2) 通过controller传递id获取最新数据,(3) 支持导航到详情页
为Flutter插件视图添加事件监听容器,自动管理事件订阅/取消订阅生命周期,避免内存泄漏并减少约84%的模板代码。适用场景:(1) 插件视图需要响应全局事件更新UI,(2) 需要避免手动管理订阅导致的内存泄漏,(3) 简化事件监听实现
| name | widget-size-checklist |
| description | 确保小组件组件根据 HomeWidgetSize 正确调整各元素大小,包括容器、图标、字体、间距、线条粗细等 |
确保小组件组件根据 HomeWidgetSize 正确调整各元素大小。
# 检查小组件大小适配
/widget-size-checklist <widget_file_path>
# 示例
/widget-size-checklist lib/screens/widgets_gallery/common_widgets/widgets/circular_metrics_card.dart
| 方法 | 说明 | Small | Medium | Large | Wide | Wide2 |
|---|---|---|---|---|---|---|
getPadding() | 外层容器内边距 | 8px | 12px | 16px | 12px | 16px |
getTitleSpacing() | 标题与内容间距 | 16px | 20px | 24px | 20px | 24px |
getItemSpacing() | 列表项之间间距 | 6px | 8px | 12px | 8px | 12px |
getSmallSpacing() | 紧密元素间距 | 2px | 4px | 6px | 4px | 6px |
getHeightConstraints() | 高度约束 | 150-250 | 200-350 | 250-450 | 200-350 | 250-450 |
| 方法 | 说明 | Small | Medium | Large | Wide | Wide2 |
|---|---|---|---|---|---|---|
getLargeFontSize() | 大标题/数值 | 36px | 48px | 56px | 48px | 56px |
getTitleFontSize() | 标题 | 16px | 24px | 28px | 24px | 28px |
getSubtitleFontSize() | 副标题/标签 | 12px | 14px | 16px | 14px | 16px |
getLegendFontSize() | 图例/小字 | 10px | 12px | 14px | 12px | 14px |
| 方法 | 说明 | Small | Medium | Large | Wide | Wide2 |
|---|---|---|---|---|---|---|
getIconSize() | 图标大小 | 18px | 24px | 28px | 24px | 28px |
getStrokeWidth() | 线条粗细 | 6px | 8px | 10px | 8px | 10px |
getLegendIndicatorWidth() | 指示器宽度 | 16px | 24px | 32px | 24px | 32px |
getLegendIndicatorHeight() | 指示器高度 | 8px | 12px | 16px | 12px | 16px |
getBarWidth() | 柱状图柱宽 | 12px | 16px | 20px | 16px | 20px |
getBarSpacing() | 柱间距 | 0.5px | 1px | 1.5px | 1px | 1.5px |
| 系数 | 默认值 | 用途 |
|---|---|---|
iconContainerScale | 2.0 | 图标容器大小 = 图标大小 × 2 |
progressStrokeScale | 0.4 | 进度条粗细 = strokeWidth × 0.4 |
final HomeWidgetSize size 参数this.size = const MediumSize() 默认值fromProps 工厂方法正确接收并传递 size 参数class MyWidget extends StatefulWidget {
final HomeWidgetSize size;
const MyWidget({
super.key,
required this.data,
this.size = const MediumSize(), // 默认值
});
factory MyWidget.fromProps(
Map<String, dynamic> props,
HomeWidgetSize size,
) {
return MyWidget(
data: props['data'],
size: size, // 必须传递
);
}
}
widget.size.getPadding()widget.size.getHeightConstraints()widget.size.getTitleSpacing()(如有标题)Container(
padding: widget.size.getPadding(),
constraints: widget.size.getHeightConstraints(),
decoration: BoxDecoration(...),
child: Column(
children: [
Text('标题', style: TextStyle(fontSize: widget.size.getTitleFontSize())),
SizedBox(height: widget.size.getTitleSpacing()),
// 内容
],
),
)
// 列表项
_MyItemWidget(
data: item,
size: widget.size, // 必须传递
)
getIconSize() * iconContainerScale)final iconSize = size.getIconSize();
final containerSize = iconSize * size.iconContainerScale;
SizedBox(
width: containerSize,
height: containerSize,
child: ...
)
size.getIconSize() 或其倍数Icon(
Icons.star,
size: size.getIconSize() * 0.8, // 可根据需要调整倍数
color: Colors.amber,
)
getTitleFontSize()getLargeFontSize() * 0.35(约 13-20px)getSubtitleFontSize()getLegendFontSize()Text(
'大数值',
style: TextStyle(
fontSize: size.getLargeFontSize() * 0.35, // 约 13-20px
fontWeight: FontWeight.w700,
),
),
SizedBox(height: size.getSmallSpacing()),
Text(
'标签',
style: TextStyle(
fontSize: size.getSubtitleFontSize(),
fontWeight: FontWeight.w500,
),
)
getItemSpacing() 或 getSmallSpacing()SizedBox(width: size.getSmallSpacing() * 2), // 行间距
SizedBox(height: size.getSmallSpacing()), // 列间距
class _MyPainter extends CustomPainter {
final double strokeWidth;
_MyPainter({this.strokeWidth = 2.5});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..strokeWidth = strokeWidth // 使用参数
..style = PaintingStyle.stroke;
// ...
}
@override
bool shouldRepaint(covariant _MyPainter oldDelegate) {
return oldDelegate.strokeWidth != strokeWidth; // 检查变化
}
}
// 使用
CustomPaint(
painter: _MyPainter(
strokeWidth: widget.size.getStrokeWidth() * widget.size.progressStrokeScale,
),
child: ...
)
MediaQuery.of(context).size.width - 32import 'package:Memento/screens/home_screen/models/home_widget_size.dart';
// 小尺寸
MyWidget(data: data, size: const SmallSize()),
// 中尺寸
MyWidget(data: data, size: const MediumSize()),
// 大尺寸
MyWidget(data: data, size: const LargeSize()),
// 中宽尺寸 (4x1) - 宽度填满屏幕
SizedBox(
width: MediaQuery.of(context).size.width - 32,
height: 220,
child: MyWidget(data: data, size: const WideSize()),
),
// 大宽尺寸 (4x2) - 宽度填满屏幕
SizedBox(
width: MediaQuery.of(context).size.width - 32,
height: 320,
child: MyWidget(data: data, size: const Wide2Size()),
),
// ❌ 错误
_MyItemWidget(data: item) // 缺少 size
// ✅ 正确
_MyItemWidget(data: item, size: widget.size)
// ❌ 错误
SizedBox(width: 56, height: 56)
// ✅ 正确
final containerSize = size.getIconSize() * size.iconContainerScale;
SizedBox(width: containerSize, height: containerSize)
// ❌ 错误
backgroundPaint.strokeWidth = 2.5
// ✅ 正确
backgroundPaint.strokeWidth = strokeWidth
// ❌ 错误
fontSize: 16
// ✅ 正确
fontSize: size.getLargeFontSize() * 0.35
// ❌ 错误 - 所有卡片都使用默认 MediumSize()
MyWidget(data: data)
// ✅ 正确 - 传递所有尺寸
MyWidget(data: data, size: const SmallSize())
MyWidget(data: data, size: const MediumSize())
MyWidget(data: data, size: const LargeSize())
MyWidget(data: data, size: const WideSize())
MyWidget(data: data, size: const Wide2Size())
// 1. 添加参数
class _MyItemWidget extends StatelessWidget {
final MyData data;
final HomeWidgetSize size; // 添加
const _MyItemWidget({
required this.data,
required this.size, // 添加
});
// 2. 使用 size 计算尺寸
@override
Widget build(BuildContext context) {
final iconSize = size.getIconSize();
final valueFontSize = size.getLargeFontSize() * 0.35;
final labelFontSize = size.getSubtitleFontSize();
final spacing = size.getSmallSpacing();
// 3. 应用尺寸
return Row(
children: [
Icon(Icons.star, size: iconSize),
SizedBox(width: spacing),
Text('值', style: TextStyle(fontSize: valueFontSize)),
],
);
}
}
// 1. 添加参数
class _MyPainter extends CustomPainter {
final double progress;
final double strokeWidth; // 添加
_MyPainter({required this.progress, this.strokeWidth = 2.5});
// 2. 使用 strokeWidth
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..strokeWidth = strokeWidth // 使用参数
..style = PaintingStyle.stroke;
// ...
}
// 3. 检查 strokeWidth 变化
@override
bool shouldRepaint(covariant _MyPainter oldDelegate) {
return oldDelegate.progress != progress ||
oldDelegate.strokeWidth != strokeWidth;
}
}
// 图标大小
final iconSize = size.getIconSize(); // 18/24/28
// 容器大小
final containerSize = iconSize * size.iconContainerScale; // 36/48/56
// 进度条粗细
final strokeWidth = size.getStrokeWidth() * size.progressStrokeScale; // 2.4/3.2/4.0
// 数值字体
final valueFontSize = size.getLargeFontSize() * 0.35; // ~13/17/20
// 标签字体的
final labelFontSize = size.getSubtitleFontSize(); // 12/14/16
// 元素间距
final itemSpacing = size.getSmallSpacing() * 2; // 4/8/12
测试时确认以下内容:
MediaQuery.of(context).size.width - 32)