关于 dartdoc-conventions
面向 Dart 项目的文档编写规范,规定公共 API 与代码注释的写法,帮助开发者维护清晰且可由分析器检查的 API 契约。
主要功能
- 01为每个公共声明添加 `///`
- 02首行使用独立摘要句
- 03说明行为、约束和异常
- 04为文档标识符添加交叉链接
- 05在实现注释中说明不变量
- 06检查导出库文档和分析器规则
适用场景
- 01新增 Dart 公共 API
- 02审查或更新代码注释
- 03准备可由 `dart doc` 生成文档的包
- 04配置文档 lint 检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ zakariaf/ flutter-skills --skill dartdoc-conventions安装后在 Claude Code 中输入/dartdoc-conventions确认已加载。
详细介绍
Dartdoc 约定——公共 API 是一份契约
公共代码被阅读的次数远多于编写的次数。没有前导 _ 的符号就是调用方所依赖的契约,因此它必须带有读者无需打开函数体就能理解的 /// 文档注释。文档注释使用 ///,并遵循 Effective Dart: Documentation。添加或更改公共声明,或编写函数体内的 // 注释时,均适用本约定。
不容妥协的规则
- 每个公共声明都必须有
///文档注释——包括公共类、构造函数、方法、getter、顶层函数、typedef 和字段。public_member_api_docs是错误,没有“显而易见的成员”例外:它会标记每个没有文档注释的公共成员,因此缺少文档注释会导致构建失败。仅仅“以防万一”就将符号设为公共会被评审拒绝——应改为_私有,这样就不需要文档注释,也不会形成契约。 - 只能用
///,绝不用/** */。 Dartdoc 只识别///。JavaDoc 块会被静默忽略,符号看起来就像没有文档注释一样。slash_for_doc_comments会标记这种写法。 - 首行必须是独立成段、以句号结尾的完整句子。 工具在 API 列表中只显示这一句,因此它必须能够独立呈现;用空白的
///行将它与正文隔开。 - 方法和函数的文档注释以动词短语开头(第三人称),例如:“Returns…”、“Schedules…”、“Loads…”、“Marks…”。布尔型 getter 或返回
bool的方法以 “Whether…” 开头。 - 绝不复述名称。 在
String name上写/// The name.,或在total()上写/// Returns the total.——都不允许。如果公共成员除了名称之外没有其他信息可补充,这就说明应该将它改为_私有(私有成员无需文档注释,这样矛盾也就消失了)。必须保持公共的成员仍然需要真正的///文档注释——绝不能让它保持公共却没有文档注释;应补充有意义的信息:单位、范围、可空性、异常和副作用,以及该符号所强制执行的不变量。 - 用
[方括号]链接标识符,以便 dartdoc 解析它们:/// Throws [StateError] if [id] is unknown; see [copyWith].。comment_references会对失效链接发出警告。 - 函数体内的
//只解释原因,绝不解释内容。 代码已经说明了内容。叙述代码的注释(// loop over items)会过时并产生错误信息。注释应说明原因、易错之处或不变量。 - 在不变量得到强制执行的位置重申它。 如果某行代码维护了某项保证——例如顺序、限制范围、先持久化再发布,或转换为标准单位——就用简短的
//说明,这样任何削弱保证的差异都会格外醒目。魔法常量也一样:注明数字的来源。 - 文档注释应与代码在同一个差异中更改。 错误的文档注释比没有文档注释更糟。你所更改的每条注释都必须仍然正确——将此项加入 PR 检查清单。
- 每个导出入口文件都应有一条库文档注释。 公共入口文件(例如
my_package.dart)应在library;指令上方添加///库文档注释。dangling_library_doc_comments是错误——没有附着到声明的前导///必须是真正的库文档注释,不能成为空行或export上方的孤立注释。
为值类型编写文档
/// A note the user has authored and can later edit or archive.
///
/// Immutable; derive changes with [copyWith]. [updatedAt] is UTC and never
/// precedes [createdAt].
class Note {
/// Creates a note; [updatedAt] must not precede [createdAt].
const Note({
required this.id,
required this.title,
required this.createdAt,
required this.updatedAt,
});
/// Stable unique identifier, assigned once at creation.
final String id;
/// Human-readable heading shown in lists; may be empty, never null.
final String title;
/// Creation instant, in **UTC**.
final DateTime createdAt;
/// Last-edit instant, in **UTC**; equals [createdAt] until first edited.
final DateTime updatedAt;
/// Returns a copy with the given fields replaced.
Note copyWith({String? title, DateTime? updatedAt}) => Note(
id: id,
title: title ?? this.title,
createdAt: createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
每个公共字段都必须带有 /// 文档注释,包括 title——在强制执行 public_member_api_docs 的情况下,字段“复述了名称”并不意味着可以省略文档注释,而是提醒你应补充实际信息(或将字段改为 _ 私有)。如果类型本身未说明单位和约束,应明确记录:货币的最小单位、0–23 小时、范围是否包含端点、null 的含义,以及“必须 > 0”。
记录行为——异常、异步和副作用
/// Schedules a reminder for [task] at [when].
///
/// [when] must be in the future; a past instant is clamped to now. Persists the
/// scheduled row before arming the OS notification, so a crash mid-call cannot
/// leave a notification the store never recorded.
///
/// Throws [PermissionDeniedException] if the OS denied notification permission.
Future<void> scheduleReminder(Task task, DateTime when) async {
// persist-before-arm: the store is the source of truth, the OS mirrors it
await _store.saveScheduled(task.id, when);
await _plugin.schedule(task.id, when);
}
说明异步行为、方法会写入或修改的内容,以及它可能抛出的每一种异常。如果函数是全函数(对每个输入都会返回,绝不抛出异常),请明确说明——这是调用方依赖的一项实际保证。
库文档注释和导出入口文件
/// Pure-Dart domain core: value types, typed failures, and total functions.
///
/// No Flutter, Riverpod, or `dart:io` import — this purity is what lets the
/// core be unit-tested without a widget harness and reused across platforms.
library;
export 'src/note.dart';
export 'src/task.dart';
/// 会附着到 library; 指令上。绝不能让 /// 孤立在 export 或空行上方。
在强制执行的位置重申不变量
如果某个符号强制执行一条规则,请将该规则写入其契约,并在函数体内维护该规则的那一行标注出来:
/// Failure modes of a checkout. Switched exhaustively by the caller, so a new
/// case is a compile error at every call site.
sealed class CheckoutFailure {
/// Const base constructor, so subclasses can be `const`.
const CheckoutFailure();
}
/// The account balance was below the order total; no charge was made.
final class InsufficientFunds extends CheckoutFailure {
/// Creates an [InsufficientFunds] failure.
const InsufficientFunds();
}
/// Read-only view of the current order; mutated only through this notifier.
class OrderNotifier extends Notifier<Order> {
@override
Order build() => Order.empty();
/// Adds [item] and recomputes the total.
///
/// Persists via the repository before emitting, so a mid-write crash cannot
/// surface a line the store never saw.
Future<void> addItem(Item item) async {
// persist-before-emit: emitted state is always a state the store holds
await _repository.append(item);
state = state.withItem(item);
}
}
反模式
/** JavaDoc-style */——dartdoc 会忽略它;该符号会被视为未编写文档,并导致public_member_api_docs检查失败。- 重复说明名称——在
String get id上写/// 获取 id。。删除它,或补充单位、范围或不变量。 - 在一句话摘要前写一段文字——第一句话必须能在列表视图中独立成立。
- 在函数体内叙述操作机制——
// 遍历项目、i++ // 递增。解释原因,否则删除。 - 没有来源说明的魔法常量——上限值写成裸
3,却没有注释指出该数字的定义位置。 - 过时的文档——描述重构前的参数,或将不变量注释放在已不再遵守该约束的代码上。
- 保留注释掉的代码以备不时之需——git 会记住。没有负责人或问题链接的
// TODO——写成// TODO(name): reason,否则删除。 - 记录私有细节,却让公共方法没有文档。
- 空行或
export上方悬空的///——会导致dangling_library_doc_comments检查失败。 - 横幅式/ASCII 艺术分隔注释——只会让文件变得臃肿;依靠结构和命名即可。
完成标准
- 所有改动代码中的公共声明都有
///文档;首行是以句号结尾的完整独立句子。 - 方法/函数文档以动词开头;布尔 getter 以 “Whether” 开头。
- 在适用处记录单位、范围、可空性、抛出异常/
Result及副作用。 - 使用
[方括号]添加标识符的交叉链接;没有失效的comment_references。 - 在强制执行不变量的位置重申这些不变量;为魔法常量注明来源。
- 不重复说明名称,不写叙述操作的
//,不保留注释掉的代码,不写无负责人的 TODO,也不使用 JavaDoc 代码块。 - 每个导出的 barrel 都有
library;文档;没有悬空的文档注释。 - diff 改动的每条注释仍然准确。
-
dart analyze --fatal-infos --fatal-warnings检查通过;dart doc为各软件包生成文档时没有警告。
相关技能
- 请参阅
lint-and-style-config,了解如何将public_member_api_docs、dangling_library_doc_comments和comment_references配置为 analyzer 错误。 - 请参阅
naming-conventions,了解这些文档所描述的角色后缀命名(Notifier/Repository/Service/Failure)。 - 请参阅
dart3-idioms-and-coding-standards,了解文档承诺的密封类型和全函数保证。 - 请参阅
error-handling-typed-results,了解方法文档必须说明的Result/Failure契约。
参考资料
- Effective Dart:文档
dart doc工具- Linter 规则 —
public_member_api_docs、dangling_library_doc_comments、comment_references、slash_for_doc_comments - 开发软件包和插件
评论
支持 Markdown