flutter:文档注释与生成接口文档

文档注释基本语法

位置

  • /// 必须写在需要注释的代码元素(如类、方法、字段等)的上一行,每行以 /// 开头。
  • 这些注释会被 Dart 工具解析,用于生成 API 文档,支持在 IDE 中显示提示(如 VS Code、IntelliJ IDEA 的悬停提示)。
  • 示例:
    /// 这是一个简单的函数,用于计算两个数的和。
    int add(int a, int b) {
      return a + b;
    }
    

参数和返回值描述

  • 文档注释常用于描述函数或方法的参数返回值异常,通常以纯文本形式说明。
  • 约定俗成的写法:
    • 参数:[paramName] 用于表示参数。
    • 返回值:通常以 Returns 或直接描述返回值。
    • 异常:使用 Throws 或类似词语描述可能抛出的异常。
  • 示例:
    /// 计算矩形的面积。
    ///
    /// [width] 是矩形的宽度,必须为正数。
    /// [height] 是矩形的高度,必须为正数。
    /// Returns 矩形的面积(宽度 * 高度)。
    /// Throws [ArgumentError] 如果宽度或高度为负数。
    double calculateArea(double width, double height) {
      if (width < 0 || height < 0) {
        throw ArgumentError('宽度和高度必须为正数');
      }
      return width * height;
    }
    

代码引用

  • 使用方括号 [ ] 来引用代码中的其他元素(如类、方法、变量),这些会被 dartdoc 解析为超链接。
  • 示例:
    /// 参见 [calculateArea] 方法获取更多信息。
    class Rectangle {
      double width;
      double height;
    
      /// 计算矩形的面积。
      double calculateArea() => width * height;
    }
    
    • [calculateArea] 会链接到对应的方法。

段落分隔

  • 每行都需要以 /// 开头,Dart 会将连续的 /// 行合并为一个文档块。
  • 空行(带 /// 的空行)会保留为段落分隔。
  • 示例:
    /// 这是一个类的描述。
    ///
    /// 这里是第二段,描述更多细节。
    class MyClass {
      // 类内容
    }
    

特殊标签@

  • @nodoc:标记某个代码元素(如类、方法、字段等)不生成到公开的 API 文档中。
  • @deprecated:标记某个 API 已废弃。
  • @since:指明 API 从哪个版本开始引入(非官方,但有时使用)。
  • @see:指向相关资源或方法,类似于 [ ] 引用。
  • @category:将 API 归类到特定类别,便于文档组织(需配合 dartdoc--category 选项)。
  • @required:标记参数为必需(通常与 required 关键字一起使用,但文档中可以用 @required 强调)。
  • @example:提供代码示例,展示 API 的用法。
  • @macro:定义可重用的文档片段(macro),用于在多个地方插入相同的注释内容,避免重复编写相同的文档说明。
  • @author:指明作者信息。
  • @todo:标记待完成的工作。
  • @version:指明当前 API 的版本。
/// 一个用于管理用户数据的类。
/// @category UserManagement
/// @since 1.0.0
class UserManager {
  /// 创建用户。
  /// [id] 是用户的唯一标识符。
  /// @required [id]
  /// @example
  /// ```dart
  /// var manager = UserManager();
  /// manager.createUser('123');
  /// ```
  void createUser(String id) {}

  /// 这个方法已被废弃,请使用 [createUser]。
  /// @deprecated
  void addUser(String id) {}

  /// 仅供内部使用,不生成文档。
  /// @nodoc
  void _internalMethod() {}
}

泛型和类型引用

  • 对于泛型类型,可以直接在注释中使用类型参数,dartdoc 会正确解析。
  • 示例:
    /// 一个泛型类,用于存储 [T] 类型的元素。
    class Box<T> {
      T value;
      Box(this.value);
    }
    

类和库级别的注释

  • 类级别/// 可以用于描述整个类,放在类定义之前。
    /// 一个用于管理用户数据的类。
    class UserManager {
      // 类内容
    }
    
  • 库级别:在文件开头使用 /// 可以为整个库添加文档,需在 library 语句(如果有)之前。
    /// 一个用于处理数学运算的库。
    library math_utils;
    
    // 库内容
    

注释的继承

  • 如果子类或实现类中的方法没有提供 /// 注释,Dart 会自动继承父类或接口中对应方法的文档注释。
  • 示例:
    abstract class Animal {
      /// 发出动物的叫声。
      void makeSound();
    }
    
    class Dog implements Animal {
      // 没有注释,继承 Animal 的 makeSound 注释
      @override
      void makeSound() => print('Woof!');
    }
    

位置敏感

位置敏感/// 必须紧邻被注释的代码元素,中间不能有空行,否则不会被解析为文档注释。

/// 错误:空行导致注释无效
// (空行)
void myFunction() {}

如何配置 dartdoc_options.yaml

dartdoc_options.yaml 是一个可选的配置文件,放在项目根目录,用于自定义 dartdoc 的行为。以下是常用配置项:

  • 在项目根目录创建文件 dartdoc_options.yaml

  • exclude:指定不生成文档的库或包。用于排除内部或测试用的库。

  • nodoc:指定不生成文档的特定类、方法或符号,类似于在代码中使用 @nodoc

  • linkToSource:为文档中的代码元素添加指向源代码的超链接。方便用户查看 GitHub 或其他托管平台上的源代码。生成的文档会在每个类或方法的定义旁显示指向 GitHub 的链接。

  • categories:定义文档的分类,配合代码中的 @category 标签。

    dartdoc:
      categories:
        TaskManagement:
          name: "Task Management"
          markdown: "docs/categories/task_management.md"
    

    TaskManagement 类别会显示在文档导航中,点击后显示 task_management.md 的内容。

  • output:指定文档输出目录。文档将生成到 docs 目录而非默认的 doc/api

    dartdoc:
      output: "docs"
    
  • showUndocumentedCategories:是否显示未在代码中标记 @category 的类/方法。设置为 false 时,仅显示明确标记了 @category 的元素。

dartdoc:
  output: "docs"
  exclude: ["test_library"]
  nodoc: ["_InternalMethod"] # 即使不标记 @nodoc,也会被隐藏
  categories:
    TaskManagement:
      name: "Task Management"
      markdown: "docs/task_management.md"
  linkToSource:
    root: "https://github.com/username/task_manager/blob/main"
    pathPrefix: "lib/"

使用 dartdoc 生成接口文档

准备好代码

/// 一个用于管理任务的库,提供了任务创建和查询功能。
/// @since 1.0.0
/// @category TaskManagement
library;

/// 表示任务状态的枚举。
enum TaskStatus {
  /// 任务待处理。
  pending,

  /// 任务已完成。
  completed,
}

/// 任务类,用于表示一个待办任务。
/// @see [TaskManager]
class Task {
  /// 任务的唯一标识符。
  final String id;

  /// 任务的描述。
  final String description;

  /// 任务的状态,默认为 [TaskStatus.pending]。
  /// @see [TaskStatus]
  TaskStatus status;

  /// 创建一个新任务。
  /// [id] 是任务的唯一标识符,必须提供。
  /// [description] 是任务的描述内容。
  /// @required [id]
  /// @example
  /// ```dart
  /// var task = Task('001', 'Write documentation');
  /// print(task.status); // TaskStatus.pending
  /// ```
  Task(this.id, String description)
    : description = description,
      status = TaskStatus.pending;
}

/// 任务管理器,负责存储和操作任务。
/// @since 1.0.0
class TaskManager<T extends Task> {
  /// 存储任务的列表。
  final List<T> _tasks = [];

  /// 添加一个任务到管理器。
  /// [task] 是要添加的任务对象。
  /// @example
  /// ```dart
  /// var manager = TaskManager<Task>();
  /// var task = Task('001', 'Test task');
  /// manager.addTask(task);
  /// ```
  void addTask(T task) {
    _tasks.add(task);
  }

  /// 获取所有任务的数量。
  /// Returns 任务列表的长度。
  int getTaskCount() => _tasks.length;

  /// 已废弃,请使用 [addTask] 代替。
  /// @deprecated
  @Deprecated('Use addTask instead')
  void insertTask(T task) => addTask(task);

  /// 内部方法,仅用于日志记录,不生成文档。
  /// @nodoc
  void _logTask(T task) {
    print('Task added: ${task.id}');
  }
}

/// 高级任务,继承自 [Task],添加了优先级。
class PriorityTask extends Task {
  /// 任务的优先级(1-5,1最高)。
  final int priority;

  /// 创建高级任务。
  /// [priority] 是任务的优先级,必须在1到5之间。
  /// Throws [ArgumentError] 如果优先级无效。
  PriorityTask(super.id, super.description, this.priority) {
    if (priority < 1 || priority > 5) {
      throw ArgumentError('Priority must be between 1 and 5');
    }
  }

  // 继承自 Task 的 [status] 注释,无需重复注释
  @override
  set status(TaskStatus newStatus) => super.status = newStatus;
}

安装dartdoc生成文档

确保安装 dartdoc

dart pub global activate dartdoc

执行命令,生成文档到 doc/api 目录或者指定目录

dart doc .

# 可以全局执行:
dart pub global run dartdoc

发送的错误

以下是我在生成文档的时候发生的错误:

(base) xieshaolin@xieshaolindeMacBook-Pro flutter_sophomore % dart pub global run dartdoc
Unhandled exception:
type 'Null' is not a subtype of type 'String'
#0      DartdocOptionContext.sdkDir (package:dartdoc/src/dartdoc_options.dart:1222:44)
#1      new PubPackageBuilder (package:dartdoc/src/model/package_builder.dart:65:23)
#2      main (file:///Users/xieshaolin/.pub-cache/hosted/pub.dev/dartdoc-8.3.4/bin/dartdoc.dart:24:7)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:312:33)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)
(base) xieshaolin@xieshaolindeMacBook-Pro flutter_sophomore % dart --version

可能是我没有配置环境变量

# 配置环境变量
export DART_SDK=/Users/xieshaolin/fvm/default/bin/cache/dart-sdk

有或者因为是我使用的是fvm的dart,需要用fvm进行管理

# 如果使用的是fvm 管理:
fvm dart doc

访问

浏览器

可以直接在浏览器打开

在点击连接的时候,浏览器没办法直接访问index.html

http访问

执行命令,安装 dhttpd

dart pub global activate dhttpd

执行命令,运行 dhttpd

(base) xMacBook-Pro flutter_sophomore % dhttpd --path doc/api
Server started on port 8080

访问http://localhost:8080/。这个时候再点击侧边栏的内容,就可以直接访问。

×

喜欢就点赞,疼爱就打赏