with one click
widget-size-checklist
确保小组件组件根据 HomeWidgetSize 正确调整各元素大小,包括容器、图标、字体、间距、线条粗细等
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
确保小组件组件根据 HomeWidgetSize 正确调整各元素大小,包括容器、图标、字体、间距、线条粗细等
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
迁移小组件到事件携带数据模式,优化刷新性能。将数据直接放在事件中传递,小组件直接使用事件数据,避免重复的缓存访问操作。
为插件主页添加带过滤器的列表小组件,使用 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)