mcpField
N
naming-conventions

naming-conventions

github.com/zakariaf开发65161 次安装6 周前更新
代码仓库

关于 naming-conventions

面向 Dart/Flutter 开发的命名规范技能,依据 Effective Dart 约定标识符、文件及架构层级后缀,帮助代码保持一致且易于搜索。

主要功能

  1. 01Dart 类型与成员命名规则
  2. 02文件与目录命名规则
  3. 03按架构层级使用类型后缀
  4. 04导入分组与排序约定
  5. 05常见命名反模式清单

适用场景

  1. 01为新 Dart 功能统一命名
  2. 02审查类型名称与架构层级是否匹配
  3. 03整理文件名和导入顺序

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zakariaf/flutter-skills --skill naming-conventions

安装后在 Claude Code 中输入/naming-conventions确认已加载。

详细介绍

命名约定

保持命名一致并体现角色,可以让代码更易搜索、更容易理解;类型名称的后缀则声明了它所属的层,因此审查者、grep 和禁止导入的检查门都能只凭名称判断层级。这项技能讲的是如何做;规范性的做什么请参见 Effective Dart。绝不要自创与语言自身规范相矛盾的内部风格。

不可妥协的规则

  1. 类型使用 UpperCamelCase。 类、枚举、混入、扩展、类型别名、类型参数:TaskScreen、OrderStatus、Predicate<T>。统一的形式能让类型在视觉上区别于值。
  2. 成员、变量、函数和参数使用 lowerCamelCase。 dueDate、loadTasks()、itemCount。这是语言默认的命名方式;偏离它会让读者多看一眼才能确认。
  3. 常量使用 lowerCamelCase,绝不使用 SCREAMING_CAPS。 const maxItemsPerPage = 50;——而不是 const MAX_ITEMS = 50。Dart 已弃用 C 语言惯例;分析器要求遵守 constant_identifier_names。
  4. 文件、文件夹、库和导入前缀使用 lowercase_with_underscores。 task_detail_screen.dart、features/task_detail/、import 'package:app_core/app_core.dart';。跨平台文件系统和 pub 都要求这样命名。
  5. 文件名应与其主要声明一致,使用蛇形命名法;每个文件只放一个主要公共类型。 TaskNotifier 应放在 task_notifier.dart 中。不要使用 utils.dart/helpers.dart/models.dart 这种杂物文件,也不要使用 utils//common//helpers//misc/ 这种杂物文件夹——读者搜索某个符号时,必须能找到拥有该符号的文件。core/ 是认可的纯基础层(值对象、Result/Failure、Clock 接口、纯计算器),不是杂物抽屉——布局由 project-structure-and-packages 说明。
  6. 超过两个字母的缩写按普通单词的方式使用大小写。 Json、Http、Url、Api → JsonOrder、HttpClient、fromJson、imageUrl——而不是 JSONOrder、HTTPClient。英语中的双字母大写缩写可以在类型中保留大写,如 ID、UI。混合大小写的缩写难以搜索,也不一致。
  7. 前导下划线表示库私有——只有确实要设为私有时才使用。 绝不要给公共符号加 _ 前缀来“命名空间”化;这样会导致它无法在其他文件中使用。公共符号(无 _)是有文档说明的契约——请参见 dartdoc-conventions。
  8. 不要在名称中使用匈牙利命名法或编码类型信息。 不要写 strName、iCount、lstItems、userMap、nameString、itemsList。类型系统已经知道类型;写 name、usersById、items 即可。
  9. 使用完整的词;在名称中体现单位和语义。 maxItemsPerPage、retryDelaySeconds、orderTotalMinorUnits——绝不要只写 max、delay、total。缩写(opt、qty、amt)只能用于一个简短纯函数的内部,并附注释说明其含义。省略单位的名称容易引发单位错误。
  10. 布尔值应读起来像断言。 使用 isLoading、hasError、canSubmit、shouldRetry——而不是 loading、error、retry。布尔值 getter 和方法以 is/has/can/should 开头,使条件读起来像一句话。
  11. 访问器名称不要以 get 开头。 暴露 dueTasks,而不是 getDueTasks()。Dart 有真正的 getter。函数使用动词短语(loadTasks()、scheduleReminder());非布尔 getter 使用名词短语(itemCount、nextDueDate)。
  12. 导入应分组并排序: 先 dart:,再 package:,最后相对路径——每组按字母顺序排列;export 单独成组,放在导入之后。使用 dart format 和 directives_ordering lint 强制执行;不要手动对抗格式化工具。

后缀声明层级

角色后缀会将名称转化为层级契约:无需打开文件,grep 或按路径限定的导入门禁就能区分仓库、视图和平台边界。实体名称使用中性的领域名词(Task、Order、Account、Item)。

角色后缀/模式层级文件 → 符号
屏幕/路由目标[Feature]ScreenUI(View)task_list_screen.dart → TaskListScreen
可复用组件[Thing](仅在含义不明确时加 Widget)UItask_card.dart → TaskCard
ViewModel(Riverpod,默认)[Feature]NotifierUI(ViewModel)task_list_notifier.dart → TaskListNotifier
ViewModel(仅限 Provider 附录)[Feature]ViewModelUI(ViewModel)task_list_view_model.dart → TaskListViewModel
仓库(接口)[Entity]Repositorydatatask_repository.dart → TaskRepository
仓库(实现)Drift[Entity]Repository/Remote[Entity]Repositorydatadrift_task_repository.dart
Drift DAO[Entity]Daodatatasks_dao.dart → TasksDao
你定义的能力接口[Concern]Serviceboundaryshare_service.dart → ShareService
服务实现(按 flavor 区分)[Provider][Concern]Serviceapp adapterfirebase_analytics_service.dart → FirebaseAnalyticsService
对特定插件/SDK/原生通道的封装[Concern]Gatewayboundarynotification_gateway.dart → NotificationGateway
Gateway 实现[Plugin][Concern]Gateway/Live[Concern]Gatewayapp adapterfln_notification_gateway.dart → FlnNotificationGateway
纯 Dart 领域逻辑[Domain] 加动词后缀(Calculator/Validator/Formatter)domainprice_calculator.dart → PriceCalculator
不可变模型领域名词,不带后缀任意task.dart → Task
故障类型[Domain]Failure(sealed)任意order_failure.dart → OrderFailure
  • Notifier 才是 ViewModel 角色——类型名称中绝不使用 ViewModel 或 ChangeNotifier。 在 Riverpod 3.x 中,ViewModel 是 Notifier/AsyncNotifier/StreamNotifier,每个 Screen 对应一个,严格保持 1:1,并在 [feature]_notifier.dart 中命名为 [Feature]Notifier(文件名对应主要声明)。[Feature]ViewModel 和 [feature]_view_model.dart 仅在 Provider/ChangeNotifier 附录中获准使用——Riverpod 路径中绝不使用,也绝不使用 [Feature]Controller。参见 state-management-riverpod。
  • Service 与 Gateway——两者都表示不依赖具体提供方的边界端口,区别在于契约由谁定义。 [Concern]Service 是你定义的能力接口(ShareService、AnalyticsService);[Concern]Gateway 是对特定外部插件/SDK 或原生 MethodChannel 的轻量封装(例如封装 flutter_local_notifications 的 NotificationGateway、封装 flutter_secure_storage 的 SecureStorageGateway)。领域代码和 UI 代码使用接口名称;具体实现(FirebaseAnalyticsService、FlnNotificationGateway)只存在于组合根中。具体实现名称泄漏到共享代码中,是可通过 grep 捕获的异味。参见 service-boundary-and-native。
  • 纯 Dart 类型使用 Calculator/Validator/Formatter/Parser 动词后缀,且不接触任何 Flutter。 该后缀表明此类型不依赖框架,可在不使用 Widget 的情况下进行单元测试。
  • 符合领域的通用语言,并在各处保持一致——模型、仓库和 UI 中每个概念都使用同一个词,确保搜索能找到所有引用。

值与时刻,以及其他通过名称表达的区别

将类型系统无法表达的语义区别编码到名称中。在预期使用稳定日历值的场景中使用原始时刻(DateTime.now()),是常见的正确性缺陷;通过命名可以清晰标示这一边界。

// A real wall-clock instant is legal only at the Clock boundary and named as one.
final DateTime capturedAt = clock.now();      // instant, from package:clock's Clock (never DateTime.now())

// A stored quantity carries its canonical unit in the name.
final int priceMinorUnits;                    // not `price` — unit is explicit
final int distanceMeters;                     // SI base unit, integer

// Unused callback params are `_` (and `__` for a second).
onChanged: (_) => notifier.refresh(),

关于规范存储方式,请参见 value-objects-money-and-units;关于通过 clockProvider 注入的 Clock(来自 package:clock),请参见 service-boundary-and-native——绝不自定义 ClockService。

示例

// task_list_notifier.dart — file name == primary declaration, snake_cased
import 'dart:async'; // dart: group, alphabetized

import 'package:flutter_riverpod/flutter_riverpod.dart'; // package: group, alphabetized

import 'task.dart'; // relative group, alphabetized

/// Frozen paging bound — unit lives in the name (rule 9).
const int maxItemsPerPage = 50; // lowerCamelCase const, not SCREAMING_CAPS

/// ViewModel for [TaskListScreen]. One Notifier per Screen, 1:1 (role suffix).
class TaskListNotifier extends AsyncNotifier<List<Task>> {
  @override
  Future<List<Task>> build() => ref.watch(taskRepositoryProvider).loadTasks();

  /// Verb-phrase command; routes through the repository's single write path.
  Future<void> archive(String taskId) async {
    await ref.read(taskRepositoryProvider).archive(taskId);
    ref.invalidateSelf();
  }

  bool get hasTasks => state.valueOrNull?.isNotEmpty ?? false; // boolean assertion
  int get taskCount => state.valueOrNull?.length ?? 0;          // noun getter, no get- prefix
}

反模式

  • 在新的 Dart 代码中使用 SCREAMING_CAPS 常量(const MAX_ITEMS = 50)——应使用 lowerCamelCase;分析器会对此发出警告。
  • 类名与文件名不一致(TaskScreen 定义在 home.dart 中),或在 utils.dart/models.dart 这类大杂烩文件中放入无关内容——会破坏从搜索结果定位文件的流程。
  • 将涉及 Flutter 的类型命名为 …Calculator/…Validator(这是纯 Dart 类型的后缀),或将 Riverpod ViewModel 命名为 …ViewModel/…VM/…Controller——后缀会错误地暗示所属层(Riverpod 路径使用 …Notifier;…ViewModel 仅用于 Provider 附录)。
  • 在共享代码或领域代码中引用具体实现名称(FlnNotificationGateway、FirebaseAnalyticsService)——应使用 Gateway/Service 接口命名;实现应放在组合根中。
  • 将 DateTime.now() 赋给本应保持稳定的字段,或让未命名的时间点进入领域逻辑——应注入 Clock 并为时间点命名(capturedAt)。
  • 使用 getDueTasks() 或 get/set 前缀——Dart 有真正的 getter。
  • 在名称中写入类型:userMap、nameString、itemsList、strName、iCount。
  • 布尔值名称缺少 is/has/can/should(loading、valid);数量没有标明单位(total、delay`)。
  • 用前导 _ 给公共符号“划分命名空间”(这会使其变为私有且无法访问);在已提交的代码中使用单字母 l/O/I,或 data/temp/foo。
  • 在单个简短函数之外使用缩写(opt、qty、msg),除非附有映射注释。
  • 手动排序或混杂导入分组——运行 dart format;遵守 directives_ordering。

完成标准

  • 类型使用 UpperCamelCase;成员/变量/常量使用 lowerCamelCase;文件/文件夹/前缀使用 lower_snake_case。
  • 文件名与其主要声明一致;每个文件只有一个主要公共类型;没有杂物文件或文件夹。
  • 架构后缀与所属层匹配且使用正确(Screen/Notifier/Repository/Dao/Service/Gateway/Failure)。
  • Riverpod ViewModel 命名为 …Notifier(而非 …Controller/…ViewModel/…VM);纯 Dart 类型带有 Calculator/Validator/Formatter 后缀且不导入 Flutter;Service/Gateway 接口不依赖 provider,实现仅限于组合根中。
  • 超过 2 个字母的首字母缩写按单词形式大小写;使用完整的词典单词;名称中包含单位和语义;在 Clock 边界命名时间点。
  • 布尔值名称读起来像断言;函数名称使用动词短语;getter 使用名词短语且不带 get 前缀。
  • 导入按 dart:/package:/相对路径分组并按字母排序;dart format 和 dart analyze --fatal-infos 均无问题。

相关技能

  • dart3-idioms-and-coding-standards — 每个声明适合使用哪种构造(sealed/record/enum),以及复杂度限制。
  • state-management-riverpod — 为什么 ViewModel 是 Notifier,以及唯一写入路径。
  • service-boundary-and-native — 不依赖 provider 的 Service 接口及其组合根实现。
  • value-objects-money-and-units — 单位名称规则背后的规范单位存储。
  • naming 相关技能:dartdoc-conventions 介绍公共符号的 /// 契约,lint-and-style-config 介绍 directives_ordering/constant_identifier_names 的强制检查,flutter-architecture 介绍这些后缀所映射的层级 DAG。

参考资料

评论

支持 Markdown