TDesign Flutter 组件 Review
依据当前 PR 的实际源码、运行结果和测试进行 Review。仅请求 Review 时只审查;已获授权的修复或 PR 更新按约定范围继续执行,不重复索取许可,也不从 Review 请求推导修改权限。
本 skill 只补充组件对齐专项规则。分支、Spec、更新日志、双版本兼容、lint 和 PR 模板仍以仓库 CONTRIBUTING.md、specs/README.md 与 tdesign-flutter-general 为准。仓库 .agents/skills/tdesign-component-align-review/SKILL.md 是唯一维护源,外部安装副本只做镜像,不得独立演化。
使用外部镜像时,文中的仓库文件和相邻 skill 引用均从目标仓库对应路径解析,不从本机安装目录寻找仓库规范。
1. 冻结证据
记录实际 checkout、Flutter PR 的 base、head、commit 和改动文件。涉及跨端 Demo 时,同时记录小程序版本或 commit、公开 Demo 入口及直接使用的模板、样式和脚本;不要把本地过期分支或单个截图当成当前基准。
区分实现事实与对齐目标:当前 Flutter 源码、运行和测试用于判断实际行为;目标以用户指定的设计稿、版本或契约为准,未指定时参考小程序公开 Demo。两者不一致时记录偏差,不以现状替代目标。跨端参考的取证顺序:
- 已确定目标的完整 Demo、设计节点及其版本;
- Demo 直接依赖的上游源码;
- API 文档仅用于解释语义,不能单独证明 Flutter 需要新增 API。
单张截图、首屏、测试覆盖率或 API 列表不能证明 Demo 完整性。无法运行或测量的内容标为未验证,不得写“完全一致”。
2. 公开 Demo 契约
按第 1 节确定的目标逐实例核对;默认采用小程序公开页面顺序:
- 页面分组、标题、说明、元素数量和顺序;
- 初始值、禁用/加载/错误状态和操作结果;
- 页面背景、间距、尺寸、对齐、颜色及深浅色表现;
- 滚动、溢出、SafeArea、键盘和手势等实际适用边界。
以官方公开页面中的 Demo 块为契约边界:ExampleModule / ExampleItem 表达公开分组、标题、描述和示例顺序,不按每个内部 Widget 机械拆分。Flutter 多余示例应删除;仍有测试价值的场景移入聚焦 Widget 测试。同步 Demo 源码、生成示例、页面测试和必要 Golden。
组件选择与布局职责
Demo 展示组件能力,不替组件补实现或修复默认样式:
- 有对应 TD 组件时优先使用,如
TButton、TText、TPopupHeader、TPopup;不以 Material 控件的默认视觉替代。Demo 自行编写的布局使用Row、Column、Padding、SizedBox、Expanded等基础原语,只负责外部排列和合理约束。 - 组件内部背景、字体、颜色、选中态、圆角、间距和对齐由组件与主题负责;不得在 Demo 中添加
Material包装、装饰、位移或样式覆盖来掩盖缺陷。明确展示自定义样式的示例可用已有 API 或 Theme,但不能替代默认样式验收。 - Demo 管理示例状态和外部组合,例如
TPopup + TPicker的草稿、确认、取消和可用高度;状态/API 的归属遵循第 3 节。此规则不禁止导入material.dart或使用 Flutter 基础机制;显式主题覆盖遵循第 4 节。
Demo“假组件符合”检查
“假组件符合”是指 Demo 通过外层样式或约束修正组件本应自己负责的视觉,导致截图看似对齐、但组件脱离该 Demo 或换用正常约束后仍不符合。逐个公开 Demo 检查组件的直接参数和祖先 Widget,并将每个外层样式归类为以下三种:
- 允许的外部布局:
Row、Column、Expanded、Padding、SizedBox、滚动容器等仅用于排列组件与示例内容,或提供页面所需的合理约束;右侧内容的背景、分隔线、图片尺寸和文本排版属于内容自身,不是组件样式补丁。 - 允许的显式自定义:明确标注为“自定义样式”的 Demo,通过组件公开参数或组件 Theme 展示逐实例 / 子树级定制;它不能代替默认 Demo 的样式验收。
- 疑似样式补丁:在组件外层使用
Material、DecoratedBox、Container装饰、DefaultTextStyle、IconTheme、Theme、MediaQuery、Transform、透明度、阴影、圆角、颜色、位移,或特殊 padding / 尺寸来复制组件内部背景、字体、选中态、指示器、边框、圆角、间距和对齐,或掩盖溢出、状态和交互缺陷。此类用法应定位为 Demo 缺陷或组件内部缺陷,不能以截图“看起来一致”通过 Review。
默认样式 Demo 必须直接使用组件默认参数 / 组件 Theme,并在相同页面约束下检查组件实际解析出的样式;发现疑似补丁时,记录组件调用、祖先布局、运行表现和最小修复归属。不能仅凭 Golden 或首屏截图判断无补丁,也不能把合法的外部内容布局误报为组件样式覆盖。
默认 Demo 中确认会改变组件视觉契约的外层样式补丁属于阻塞问题:应优先修复组件 / 组件 Theme,或移除错误的 Demo 样式;只有明确的自定义样式示例才可保留这类覆盖,并且不能替代默认 Demo 验收。
页面壳及可复制示例按以下规则处理:
- 页面采用“页面底色 + 组件容器背景”的紧凑布局时,先检查并复用
ExamplePage.compactDemo;不要在每个 builder 中重复绘制背景,也不要仅为单个组件扩展ExampleItem。 - 连续 Cell 使用
TCellGroup管理背景、分隔线和末项边界,不在 Demo 中重复编写Column + Divider。状态子标题仍按官方页面层级放置,不能为了代码短而改变背景层级。 - Demo builder 只展示用户可复制的组件组合,Example 页面基础设施不得进入生成代码片段;生成片段的校验见第 5 节。
- 只有现有 Example 页面模式无法表达,并且职责归属与复用证据表明能力应由公共页面基础设施承担时,才考虑扩展通用 Example API;不得仅按受影响组件数量作判断,也不得用一次 Demo 修复制造新的公共抽象。
先判断缺口层级,再决定修改方式:
| 层级 | 判断 | 优先处理 |
|---|---|---|
| Demo 缺口 | 组件已有能力,但父布局约束或组合错误 | 修正 Example;如弹层未给标题预留高度 |
| 内部缺口 | 公开契约足够,但组件在合理约束下仍异常 | 修复组件并补组件测试;如 TD 标题继承错误装饰,不在 Demo 包装补救 |
| API 缺口 | 公开 Demo 或已验证的 Flutter 真实用例无法由组合、Theme 或现有 API 合理表达 | 提出最小公开 API;仅在超出既有授权或需要用户决策时确认 |
| 无缺口 | Flutter 已有等价表达 | 不修改 API |
| 候选能力 | 仅见于上游 API 表,公开 Demo 未使用且没有已验证的 Flutter 真实用例 | 只记录,不实现 |
禁止为了 API 一一对应、缩短 Demo 代码或补齐上游 props/events 而扩大 Flutter 公共面。
3. API 收敛与 Flutter 模式
不能只检查本次新增字段。结合组件当前全部公开构造参数、字段、回调、枚举、Controller 和 ThemeData,回答“API 是否已收敛”,并列出证据:
| API/组合 | 状态源和实际语义 | 重叠或冲突 | Flutter 惯用表达 | 结论 |
|---|---|---|---|---|
| 示例 | 默认值、空值和生效条件 | 独立/重复/别名/冲突 | 参数、nullable callback、Widget、builder、Theme 或 Controller | 保留/合并/改名/删除/待确认 |
必须检查:
- 同一状态、启停条件、完成事件或错误结果只有一个权威入口;
- 名称、dartdoc、默认值、空值语义、Theme 默认值和运行逻辑一致;
- 不存在不同名称表达同一能力,或参数可由另一个参数直接推导;
- callback、统一状态回调和 Controller 不重复通知或形成双完成源;
- 业务编排、Demo 状态、子组件 ThemeData、第三方类型和内部适配对象不泄漏到公共 API;
- 声明式状态优先使用不可变参数和 nullable callback;内容扩展使用 Widget/builder;可复用的样式默认值优先来自语义 token 或组件 Theme,实例 API 保留有真实逐实例配置需求的参数、必要语义选择器或完整样式逃逸入口;Controller 只处理无法由声明式重建表达的跨树命令或生命周期协调;
- 名称优先采用 Flutter、Material/Cupertino 和仓库同类组件的惯用语义,不机械沿用小程序名称。
状态所有权优先于 token 化
先确定公开 API 的单一状态源,再决定内置默认样式从哪里取得;不得反过来为了 token 化扩大公共面:
- 样式 token 化不得制造第二公开状态源;组件已有必要实例参数时,默认值可以在内部引用语义 token,但 ThemeExtension 不再暴露同义字段。
- 连续尺寸、进度等确有逐实例配置需求的值可以保留为实例参数;“属于可见样式”本身不是迁入 Theme 或删除实例参数的充分理由。
- 只有存在已验证的子树批量默认需求时,才评估由 Theme 提供默认值。实例参数必须允许未指定,不得同时保留非空实例默认值和同义 Theme 默认值形成两个默认源;视觉字段完整解析链见第 4 节,语义选择器见下节。
variant、colorScheme、status 与 Theme 的判定模型
TButton 只提供职责拆分参考,不是所有组件枚举取值的命名模板。Web / 小程序的 theme、type 等名称不能机械映射;必须根据调用者意图和运行效果判断维度,再记录枚举全部取值、默认值、空值语义、有效组合和公开字段类型。
| 维度 | 判定标准 | 示例 | 所有权 |
|---|---|---|---|
variant |
改变结构、布局、边框或填充/描边等绘制处理;不是任意色相切换 | fill / outline / text / ghost、solid / tinted、linear / circular | 实例 API;满足严格条件时 Theme 可提供 defaultVariant |
colorScheme |
业务含义和绘制处理不变,只选择一组协调的预设颜色 | defaultTheme / primary / danger,以及组件确有需要的其他调色预设 | 仅实例 API;Theme 不保存该选择器 |
status / state |
表达组件、内容或数据当前所处的业务或生命周期状态 | normal / info / success / warning / error、ready / uploading | 实例、数据模型或 Controller 中唯一合适的一处;Theme 不拥有状态 |
style / 具体样式字段 |
完整或局部覆盖最终呈现 | ButtonStyle、颜色、文字样式、间距 |
实例完整 style 或组件 Theme;不得再造同义选择器 |
按以下顺序判定,不能只看枚举成员名称:
- 值是否描述“当前发生了什么”,或会决定默认图标、提示语、无障碍语义、交互和生命周期?是则属于
status/state,即使当前实现暂时只改变颜色。 - 值是否改变填充、描边、文本、层级、布局或绘制处理?是则属于
variant。例如同一组件的实色、浅色填充和描边可以是不同 variant。 - 前两项都不成立,只在相同状态和相同绘制处理中替换协调调色板,才属于
colorScheme。
因此 info / success / warning / error 既不能一律判为状态,也不能一律判为配色:输入框校验、上传进度或公告状态属于 status;Tag 或 Popover 若只是由调用者选择视觉调色板、没有状态行为和默认内容语义,可以属于 colorScheme。light 也不是固定维度:表示浅色填充处理时属于 variant,表示一套独立调色板时才属于 colorScheme。dartdoc 必须写清实际语义,不能让调用者依赖猜测。
Theme 与默认值遵循以下所有权规则:
- 组件不得公开
colorTheme,也不得在组件 ThemeExtension 中保存组件枚举型colorScheme、defaultColorScheme、status或defaultStatus。Flutter 官方ThemeData.colorScheme与类型ColorScheme是 Material 实际调色板,不受此限制;内部私有/常量形式的内置默认配色也不是 Theme 选择器。 - ThemeExtension 可以保存具体
Color、TextStyle、ButtonStyle、布局值和按状态派生的样式。resolve(context, status: instanceStatus)接收实例状态计算样式不表示 Theme 拥有状态;Theme 不得自行选择或覆盖当前状态。 - Theme 仅在 variant 是稳定的呈现偏好、存在真实的子树批量默认需求、且实例
variant为 nullable 时,才可提供defaultVariant。解析顺序必须是instance.variant ?? theme.defaultVariant ?? builtInDefault。实例已经使用非空内置默认值时,不再增加 ThemedefaultVariant形成第二默认源。 colorScheme与status不提供 Theme 回退,使用实例值或组件内置默认值。枚举成员词汇按组件语义决定;Button 的取值不是强制全集。已发布的defaultTheme等名称保持兼容,重命名必须按 breaking change 处理。- 只有
variant、colorScheme、status彼此独立且至少存在两组有意义的交叉组合时,才同时公开。若组合被禁止、没有真实用例或一个维度可由另一个推导,则合并或只保留权威入口。 - 新增或正在修改的契约必须遵守本模型。未触及的已发布历史 API 若不符合规则,记录为技术债,不自动阻塞无关 PR,也不能作为复制先例;当同一组件契约进入修改范围时,必须评估迁移。若迁移会造成未授权的范围扩大,则明确记录独立 breaking 方案并停止扩散,而不是悄悄删除或继续新增重复入口。
以下情况默认视为冗余:
enableX与onX != null同时控制能力;disabled与 nullable callback 重复表达禁用;- 单项事件回调与统一状态回调重复报告同一事件;
- 实例参数与 ThemeExtension 在没有真实子树默认需求时重复保存同一个状态、选择器或样式标量;完整的 Flutter 样式对象逃逸入口不在此列,但必须有明确覆盖优先级;
- Controller 同时与 Future/callback 决定完成状态;
- 高层组件已表达能力,同时又公开第三方底层配置。
未发布的冗余 API 直接收敛;已发布 API 必须说明 breaking 风险和迁移方式。没有逐项证据时,不得断言“API 已完全收敛”或“符合 Flutter 设计模式”。
4. Theme 与视觉
具体视觉字段的解析优先级为:实例显式参数或完整 style > 当前子树的组件 ThemeExtension 显式字段 > 调用者显式提供且语义适用的 Flutter 标准主题字段(如 TextTheme、IconTheme、Material 组件 Theme 或 ColorScheme)> TDesign 语义 token / 已文档化的内置默认值。Flutter 自动生成的 Material 默认视觉不应覆盖 TD 默认样式;第 3 节的语义选择器不套用此视觉字段解析链。
- token 是最终兜底,不应预填到高优先级 ThemeExtension 并遮蔽 Flutter 主题继承;
- ThemeData 只承载视觉、布局和稳定的呈现默认值,不承载内容、业务状态、回调、Controller 或业务流程开关;动画时长等运动视觉参数可以进入 Theme,但是否启用业务能力仍由组件状态或回调表达;
- 组合组件应让
TText、TLoading等子组件继承当前 Theme 子树,不接收子组件 ThemeData; - 比较视觉时固定视口、DPR、字体缩放、主题、语言和状态;页面壳的合理平台差异不机械对齐。
显式主题字段与自动补全
视觉优先级按字段生效;样式对象非空不表示全部字段均为显式覆盖。Flutter 默认主题生成、copyWith / apply 补全、TDesign Theme 投影和继承链带入的值仍属于默认来源。
- 局部配置只覆盖实际配置字段,不得顺带改写选中、未选中、禁用、错误等状态的默认样式;
- 组件内部产生的默认样式不能通过实例
style、便利参数或其他高优先级入口回传给子组件,否则会把默认值伪装成实例显式覆盖; - 组合组件使用共享解析器或明确的字段级合并,不在各消费组件复制近似判断;无法区分显式值与补全值时修正共享解析边界,不增加公开参数规避。
- 聚焦测试覆盖局部主题配置和最终布局或绘制结果;共享消费者、token 与 Golden 证据按本节及第 5 节既有规则执行。
ThemeExtension 默认值与插值
ThemeExtension 的配置值、运行时有效值和插值值必须使用同一默认语义。nullable 字段以 null 表示内置默认值时,lerp 使用运行时有效默认值,不能把 null 当作 0、空颜色或其他无关初值;两侧均为 null 时保持 null。
- 连续字段按有效值插值,离散字段采用明确切换点;中间结果满足构造和布局、绘制约束;
- 默认值变更同步消费端回退、
copyWith、lerp、dartdoc 和测试; - 测试覆盖显式值、双向 nullable、两侧
null、端点与中间值,以及动态 Theme 切换后的实际呈现。
默认样式值与 token
默认值优先复用 TDesign 语义 token;无匹配 token、无子树默认需求且共享 token 缺少复用证据时,可保留单一、已文档化且有契约证据的内置默认值。不得借用无关 token 或只为消除字面量新增 Theme 字段。公开字段归属仍按第 3 节判断。
- Review 新增或修改的颜色、字体、行高、字重、圆角、阴影、间距、内边距、宽高、指示器尺寸、边框和描边;默认实现不得散落多个直接决定同一可见样式的常量。单一、已文档化且有契约证据的内置默认值不等于散落硬编码。
- 尺寸能由同一语义下的字体、行高和间距自然计算时可以组合;数值相等不代表语义相同。
0、数量、最大行数、枚举值和无量纲绘制比例不属于样式 token。固定路径坐标或数值一旦决定可见尺寸、间距、圆角或描边,仍按样式值审查;几何比例应从画布、控件尺寸或 token 派生。- 修改共享视觉原语时,列出全部消费组件并验证默认视觉和自定义主题路径,不能只证明当前 Demo 正确。
- token 化修复至少补两类证据:自定义相关 token 后实际布局或绘制值随之变化;默认 token 下 Flutter 3.32.0 Linux Golden 无意外差异。仅把数字改写为 token 名称不算完成。
5. 测试、覆盖率与 Golden 门禁
Widget 与交互测试
- Demo Widget 测试逐项断言公开分组、文案、实例数量、顺序、关键参数、初始状态和操作结果;不能只断言某种组件类型曾出现。
- 可滚动页面必须遍历完整 scroll extent,覆盖首屏外实例;动态、受控、禁用、加载、错误和主题切换场景按真实调用流验证。
- 聚焦组件测试覆盖根因、普通路径、边界、回调次数、Controller 所有权和生命周期。Demo 测试验证公开组装,不能替代组件测试。
“查看代码”的完整性与真实性
组件整体 Review 必须同时检查运行中的 Demo 和用户实际打开的“查看代码”内容。仓库里存在完整源码,不等于代码面板已展示完整示例;此项适用于所有公开示例,不只适用于弹层组件。生成机制与命令以通用 skill 第七节为准。
检查路径:逐个公开 Demo 确认 ExampleItem / builder → 实际代码入口(含 methodName 等覆盖配置)→ 生成片段 → 面板呈现内容,再追踪片段引用的辅助方法、数据和状态。不能假定生成器递归收集调用依赖,也不能只检查 @ExampleCode 是否存在。仅改变入口名称或给辅助方法加注解,不代表依赖已经齐全。
完整性边界:示例应让用户理解并复现它声称演示的能力,允许采用以下两种明确形式:
- 完整示例:包含必要导入、数据、状态宿主、触发入口、组件组合、回调和适用的生命周期清理,可在声明的依赖与最小应用壳中编译运行。
- 核心片段:可省略通用应用壳和无关页面基础设施,但必须标明省略内容、参数来源、接入方式,必要内容由当前项目实际运行的源码生成并在面板内展示,不添加外部源码地址指引来补充缺失实现。不得把核心片段标为“直接复制即可运行”。
无论采用哪种形式,当前演示能力的关键逻辑不能只藏在链接或未展示的私有方法中:
- 展示所用数据的结构及有代表性的初始值;长数据集可展示代表性数据及其结构,不要求逐段复制。
- 展示状态由谁持有、如何传入组件、如何接受回调并重建。只给
value/onChanged参数名,不能证明受控示例可复现。 - 操作后才出现内容的示例,应展示触发方式及实际组件组合;确认、取消、草稿提交、异步完成或资源释放等仅在该示例涉及时检查,不强加给静态组件。
- 私有辅助方法允许使用,但其必要实现或明确的公共替代调用必须可获得;不能留下来源不明的
_cell、_showPicker、_values等引用。参数化方法必须说明调用处如何提供数据和回调,不能只把隐藏状态改为未说明的参数。 - 代码必须来源于实际运行的 Demo,保持 API、默认值、主题和行为一致;不手工维护一份只用于展示的平行实现。不得为拼接片段而重复运行逻辑、扩增组件 API 或仅服务单个组件的示例框架 API。
例如,Picker 面板仅展示 TCellGroup(cells: [_cell(...)]) 时缺少核心能力;仅改为展示 _showPicker(items, value, onConfirm),却未说明触发入口、数据和状态回传,也仍不完整。应展示真实的触发与 TPopup + TPicker 组合,以及草稿、确认和取消的状态关系;这不要求 Picker 自身拥有 Popup。
验证与记录:
- 修改源示例后运行生成器及
--check;生成片段不得手工修改。--check只证明源码与产物同步,不证明依赖完整、能编译或行为正确。 - 实际打开各代码入口,核对是否错绑、空白、缺失、陈旧或只包含外层包装;多个示例复用同一片段时,确认各自的数据、配置与差异均有说明。无法打开时可以先核对映射和产物,但须记录面板未验证,不宣称完整验收。
- 对新增或修改且声称可运行的示例,在最小宿主中编译并复现关键操作;核心片段连同其声明的接入代码验证。复用现有组件和 Demo 测试,不机械复制整套测试;入口易失配时补充面板到实际片段的回归,不能仅断言文件名或关键词存在。源码地址指引等示例编写规范由 Review 检查,不为此添加 URL 禁用或文案匹配断言;测试聚焦入口加载和实际行为。
- 记录“公开示例 → 展示入口/片段 → 必要依赖 → 编译与关键操作证据 → 缺口”。组件整体审查覆盖所有公开示例;局部 PR 审查覆盖受影响入口及共享映射,不扩大到无关组件。
缺陷判定:按第六节的严重性规则,当前范围内的错绑、缺失、核心行为隐藏、无法解析的必要依赖、源码与展示行为不一致,或声称可运行却不能编译/复现,属于示例交付缺陷。组件整体验收或本次修改的示例不得以“仓库以前也这样写”为理由降为建议;与局部 PR 无关的历史示例缺陷单独登记,不阻塞无关改动。合理省略通用壳、已说明的数据集引用和纯排版改进不自动构成阻塞。缺陷应定位到源示例或映射配置,并明确是示例展示问题还是组件运行问题,不能从展示不完整推导组件 API 有缺口。
CI 回归登记
新增或修改组件测试、Demo 功能测试与 Golden 后,按 tdesign-flutter-general 第八节 核对登记、自测与真实 CI 入口。区分已登记、本地已通过和远端 CI 已通过,不以 job 名称代替执行证据。
当前 PR 新增或修改的组件测试、覆盖率目标或 Demo Golden 未登记到仓库已有 CI 调度入口时,属于测试门禁缺失,Review 判定为阻塞问题;不得降级为仓库既有基础设施建议。
共享测试文件可能被多个组件清单重复调度。失败归因以实际测试文件、测试名称、失败像素和差异图为准,不以调度器的组件标签或汇总名称推断多个组件同时回归;报告时区分真实受影响组件与重复呈现的同一失败。
覆盖率
- 覆盖率按改动所有权过滤并分别报告:组件生产源码、Demo 页面源码和其他依赖不能混为一个总数。
- 修改生产组件源码时,执行通用 skill 第八节登记的覆盖率门禁,以当前脚本阈值为准。报告过滤后的 LCOV 分子、分母和比例,Demo 页面覆盖率不能替代组件生产源码达标。
- 覆盖率只证明代码行执行,不能替代交互、视觉、真机或跨版本证据。测试失败时不得仅凭已生成的 LCOV 宣称门禁通过。
验证分层与执行门禁
验证以“变更批次”和当前源码指纹为单位,不按每次保存文件或每个小改动自动重复运行。先根据 git diff --name-only 和实际影响判断门禁范围:
| 变更范围 | 实现阶段优先验证 | 稳定后是否需要 Golden |
|---|---|---|
| 逻辑、状态、回调、API、测试或注释 | 受影响的聚焦功能 / Widget 测试,必要时 analyze 和生成器 --check |
不涉及视觉时跳过 |
| 组件绘制、Theme、Demo 布局、字体、图标或资源 | 先完成上行的功能门禁,再运行受影响的非视觉回归 | 仅在视觉确实受影响时运行 |
| 依赖、SDK、公共 Theme 或共享基础设施 | 按依赖链扩大到对应包 / 双版本门禁 | 由实际视觉影响决定 |
遵循以下执行顺序:
- 实现循环:可以合并多个相关编辑后一次运行聚焦测试;出现 analyze、编译或功能失败时先停止,不启动 Golden。只改变逻辑且不影响绘制时,不为“保险”重复生成或比对快照。
- 稳定检查点:实现者确认本批次代码已基本定型后,先检查
git diff --check、生成产物--check(若适用)和受影响功能测试,再一次性运行本次范围所需的完整非视觉门禁。把测试结果关联到当前源码 / diff 指纹;指纹至少包含验证基线到当前提交的二进制 diff(可用git diff --binary --no-ext-diff <base>...HEAD -- <受影响路径> | shasum -a 256记录)以及 Flutter SDK、依赖、字体/资源、主题环境和测试命令。上述任一项变化都会使检查点失效,必须重新走对应层级。 - Golden 比对:只有稳定检查点通过且变更确实影响视觉时,先在不带更新参数的情况下运行既有 Golden,检查实际图、基线图和差异图。测试失败、实现仍在调整或差异原因不明时不得生成 / 更新基线。
- Golden 更新:仅当差异来自已确认且有意的最终视觉变更,并得到当前任务授权时才运行更新命令;更新只执行一次,随后立即用不带更新参数的同一命令复跑,确认可复现。更新命令不得作为修复失败测试或掩盖未完成实现的手段。
- 结果复用:同一源码 / diff 指纹、同一 Flutter 版本、同一测试命令和同一环境下已有成功结果可以直接引用;仅查看、解释或更新 Review 文案时不得重跑测试或 Golden。环境、SDK、依赖、生成产物或相关源码变化时,按失效规则重新验证。
变更范围可先用 git diff --name-only <base>...HEAD 分流:命中 tdesign-component/lib/src/ 或 test/components/ 时跑受影响组件 Widget 测试及组件回归;命中 example/lib/ 或 example/test/ 时跑对应 Demo 功能测试与生成器 dart run tool/generate_example_code.dart --check;命中 tool/component_test_manifest.dart、视觉测试或 Golden/字体资源时,再纳入对应 visual runner。组件源码、Demo、Theme、字体或 Golden 变化稳定前只跑功能门禁,稳定后才执行一次无参 Golden 比对;清单登记、本地通过和远端 CI 通过分别记录,不能互相替代。
本地验证是分层证据,不替代远端 CI;报告必须写明实际运行的层级、命令、版本和是否跳过 Golden 的原因。Golden 不是所有代码变更的必经步骤,但对已确认的视觉变更也不能用功能测试替代。
Golden
- 功能断言与像素比较必须解耦;混合文件应拆分或用标签分流。执行平台见下方“执行环境”,不能因 Golden 的平台限制缩减功能测试矩阵。
- Golden 固定 viewport、device pixel ratio、字体缩放、语言和主题,同时覆盖 light / dark;加载确定性的中文字体和实际使用的图标字体,基线中不得保留缺字方框。
- Golden 边界包含真实 Demo 页面背景、标题、描述、组间距和组件容器。长页面按稳定
ExampleItem/ 分组边界分别截图,或生成完整滚动页面证据;仅覆盖首屏不能宣称整页齐备。 - 对弹窗、操作面板、下拉层、消息等“操作后才可见”的状态,必须先通过真实点击、选择、翻页或关闭操作触发目标状态,再断言目标内容可见并保存 Golden;初始隐藏状态或仅渲染触发按钮不能替代操作后快照。
- 首次生成或有意更新前,先与当前官方 Demo 和真机渲染核对。检查实际图、基线图和差异图,确认变化来自预期源码;不得直接运行更新命令消除失败。
- 更新基线后,立即在不带更新参数的情况下重跑同一测试,证明基线可复现。报告比较器容差、实际 diff 比例及仍未覆盖的平台风险。
- 以仓库实际 Golden comparator 和 CI 配置为准;未配置容差时默认要求精确一致。配置了容差也必须报告来源和实际差异比例;结构、数量、顺序、关键尺寸、缺字、裁切和未覆盖区域不得通过面积容差豁免。
执行环境
- Flutter 3.32.0 与 latest 均运行严格 analyze 和非视觉功能/交互测试;自动 Golden 与其他像素比较测试仅在 Flutter 3.32.0 Linux 执行和更新基线。其他平台允许真机运行、截图与人工核对,但不能写回 Golden 基线。区分本地与 CI 证据,清理本次无关产物,不处理用户已有文件。
6. Review 结论与交付
Review 输出按严重程度列出问题,每项包含文件行号、可复现行为、根因、影响、最小修复和应补测试。明确区分:
- 阻塞问题:行为、API、兼容性、视觉契约或测试证据不满足;Review 不通过;
- 非阻塞问题:不影响当前契约的风险或后续建议;单独列出,不伪装成缺陷;
- 未验证项:缺少运行、截图、版本或环境证据。
先判断缺失证据是否属于本次验收范围:完成当前行为、API、视觉或兼容性契约所必需的证据缺失时,必须列为阻塞问题;只有明确超出本次范围,或不影响当前契约且因环境限制无法补充的证据,才列为非阻塞的未验证项。不得用“未验证”绕过本 skill 的完成条件。
当 Review 没有阻塞问题时,结论为通过;除非用户明确要求,不修改 PR。用户明确要求创建或更新 PR 时,PR 模板、更新日志和 breaking 格式遵循仓库 CONTRIBUTING.md、PR 模板及 tdesign-flutter-general,本 skill 只补充以下对齐 Review 专项要求:
- 标题遵循 Conventional Commits,概括当前源码实际产生的用户可见或工程变更;
- 描述根据当前 diff、最终行为、API 影响和测试证据填写;
- 标题和描述不得把“对齐小程序”“参考小程序”或跨端比较过程当作变更内容;小程序只属于 Review 证据,不属于 PR 实现说明;
- 不写源码未实现的能力,不沿用过时标题或计划;平台专属的 Issue 关联规则按仓库规范执行;
- 更新后同时检查正文原始 Markdown 和渲染结果,再核对 title、body、base/head、commit 和文件范围。
CI 自动修复、合并冲突、生成产物回写或机器人提交都会产生新的 Review head。旧 head 上的 CodeBuddy、人工 Review、CI 或本地验证不能直接作为新 head 的通过证据;先检查自动提交的真实 diff,再对最终 head 重跑受影响门禁并重新请求所需 Review。autofix 显示成功时仍须确认它没有删除或回退本次公开契约。
当 Review 有阻塞问题时,不得用改标题、正文、Golden 容差或更新基线规避问题;仅审查任务报告问题,已授权修复的任务继续修复。修复后基于最新代码及验证证据重新判断,不能沿用旧结论。
按本次任务范围判断完成:公开 Demo 契约有证据;缺口分层正确;API 无重复状态源且符合 Flutter 模式;Theme 继承合理;相关非视觉测试、必要的 Golden(仅视觉影响时)以及双版本 analyze 和必要覆盖率按执行门禁通过。涉及 PR 时还需核对标题与描述是否准确;存在未获授权的修改需求应报告,不能据此自行修改远端。