欢迎关注微信公众号:FSA全栈行动 👋
一、痛点
如果你在开发 App A、App B、App C 时,发现自己总在不停地复制相同的 validation.dart 或者 network_helper.dart,那么这就是一个非常明显的信号:你该写 Package 了。
写代码追求复用是本能,但如果只是简单地把逻辑抽离出来,这仅仅是完成了"代码搬运"。真正想开发一个让开发者愿意信任、甚至直接加入其 Dependency 列表的优质 Package,远比写出能跑的代码要难得多。你需要面对的是 API 设计、命名规范、文档完备性、单元测试、版本管理以及极其折腾的长期维护工作。
二、概念辨析
在开始动手之前,得先搞清楚三个容易混淆的概念:Dart Package、Flutter Package 和 Flutter Plugin。
| 特性 | Dart Package |
Flutter Package |
Flutter Plugin |
|---|---|---|---|
| 底层逻辑 | 纯 Dart 代码 |
依赖 Flutter 框架 |
需要调用原生系统能力 |
| 支持环境 | Dart CLI、Server、Flutter |
仅 Flutter |
Android、iOS、Web 等 |
| 核心内容 | 算法、工具类、业务逻辑 | Widgets、Themes、动画 |
Kotlin、Swift、JS 代码 |
| 适用场景 | 校验器、API 客户端 | 自定义 UI 组件、导航助手 | 相机、蓝牙、GPS、文件系统 |
简单来说,如果你写的逻辑不需要 Flutter 的 UI 库也能跑,就选 Dart Package;如果需要写些自定义 Widget,就用 Flutter Package;如果涉及到调用手机的硬件功能,那就必须上 Plugin。
三、实战:如何打造高质量的 Package
1、目录结构与 API 设计
一个规范的 Package 结构是其专业性的第一体现。
lua
my_package
├── lib
│ ├── my_package.dart <-- 公开入口
│ └── src
│ ├── validators.dart <-- 实现细节
│ └── models.dart
├── test <-- 测试目录
├── example <-- 示例工程
├── pubspec.yaml
└── README.md
这里有个非常关键的细节:必须使用 lib/src 目录。
在 lib/my_package.dart 中,你应该通过 export 来暴露你需要给用户使用的 API:
Dart
library my_package;
export 'src/validators.dart';
export 'src/models.dart';
为什么要这么做? 因为 lib/src 下的内容对外部是不可见的。如果你直接把所有文件都放在 lib 下,用户可能会不小心 import 你的内部实现逻辑。一旦用户依赖了你的内部逻辑,你以后进行重构时,只要稍微动一下 src 里的东西,就会导致成千上万个使用你的 Package 的项目直接编译失败。
API 设计准则: 保持"简单路径"尽可能简单。 如果一个逻辑只需要 EmailValidator.isValid(email) 就能解决,就不要强迫用户去初始化一个复杂的 ValidatorManager 和一堆 Configuration 对象。
2、测试:质量的护城河
没有测试的 Package 几乎没有生命力。你不能只测"正常流程"(Happy Path),更要死磕各种边界情况:
-
输入空值会怎样?
-
网络超时了会抛出什么异常?
-
传入非法配置是否会报错?
建议采取多层次测试策略:
-
Unit Tests:针对单个类和函数的逻辑。
-
Widget Tests :如果是
Flutter Package,必须测试UI交互。 -
Integration Tests:验证整体流程是否跑得通。
3、文档:你的门面
README.md 是开发者看到你的第一眼印象。一个优秀的 README 必须在几秒钟内回答:
-
这个库是干嘛的?
-
我怎么安装?
-
怎么快速跑通一个例子?
千万不要写那种只有一句话的 README,比如"这是一个 Flutter 库"。你要写的是"一个强大且灵活的 Flutter 库,用于处理 API 重试、缓存和错误管理"。
四、发布与维护:从 pub.dev 到长期迭代
1、语义化版本 (Semantic Versioning)
在 Package 的世界里,版本号就是你的"契约"。请务必严格遵守 MAJOR.MINOR.PATCH 规则:
-
PATCH(1.0.1) :修复Bug或优化文档,不改动现有API。 -
MINOR(1.1.0):增加了新功能,但没破坏老用户的代码。 -
MAJOR(2.0.0) :引入了Breaking Changes(破坏性变更),比如改了方法名或删除了旧方法。
避坑指南: 绝对不要在 MINOR 版本里搞破坏性变更。一旦你破坏了老用户的代码,用户就不敢轻言升级,你的生命力也就到头了。
2、发布前的最后检查
在执行 dart pub publish 之前,我习惯性会跑一遍这个标准流程:
Bash
dart format . # 格式化代码
dart analyze # 静态检查
dart test # 运行测试
dart pub publish --dry-run # 模拟发布,检查配置是否完整
特别是 --dry-run,它能帮你发现有没有漏掉 LICENSE、CHANGELOG 或者 pubspec.yaml 配置错误。
3、长期维护:不仅仅是发布
发布到 pub.dev 只是开始。你会面临 Issue 反馈、Pull Request、版本更新以及对新版 Flutter 的适配。
关于 API 的演进: 当你确实需要重构一个旧的 API 时,不要直接删掉它。正确做法是先使用 @Deprecated 标记它,引导用户迁移:
Dart
@Deprecated('请使用 create() 方法代替 initialize()')
void initialize() {
create();
}
等到下一个 MAJOR 版本,再彻底移除它。
五、最后
写 Package 的本质不是为了写代码,而是为了构建一个"产品"。你的用户是开发者,而开发者的体验(DX, Developer Experience)决定了你的 Package 能走多远。
如果你的 Package 能做到:解决问题清晰、API 简单好用、文档直观、测试完备 ,那么它就能在 pub.dev 的生态里扎下根来。
如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~