从代码复用走向生态:Flutter Dart Package 实战全指南

132 阅读5分钟

欢迎关注微信公众号:FSA全栈行动 👋

一、痛点

如果你在开发 App AApp BApp C 时,发现自己总在不停地复制相同的 validation.dart 或者 network_helper.dart,那么这就是一个非常明显的信号:你该写 Package 了。

写代码追求复用是本能,但如果只是简单地把逻辑抽离出来,这仅仅是完成了“代码搬运”。真正想开发一个让开发者愿意信任、甚至直接加入其 Dependency 列表的优质 Package,远比写出能跑的代码要难得多。你需要面对的是 API 设计、命名规范、文档完备性、单元测试、版本管理以及极其折腾的长期维护工作。

二、概念辨析

在开始动手之前,得先搞清楚三个容易混淆的概念:Dart PackageFlutter PackageFlutter Plugin

特性Dart PackageFlutter PackageFlutter Plugin
底层逻辑Dart 代码依赖 Flutter 框架需要调用原生系统能力
支持环境Dart CLIServerFlutterFlutterAndroidiOSWeb
核心内容算法、工具类、业务逻辑WidgetsThemes、动画KotlinSwiftJS 代码
适用场景校验器、API 客户端自定义 UI 组件、导航助手相机、蓝牙、GPS、文件系统

简单来说,如果你写的逻辑不需要 FlutterUI 库也能跑,就选 Dart Package;如果需要写些自定义 Widget,就用 Flutter Package;如果涉及到调用手机的硬件功能,那就必须上 Plugin

三、实战:如何打造高质量的 Package

1、目录结构与 API 设计

一个规范的 Package 结构是其专业性的第一体现。

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

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 必须在几秒钟内回答:

  1. 这个库是干嘛的?

  2. 我怎么安装?

  3. 怎么快速跑通一个例子?

千万不要写那种只有一句话的 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 之前,我习惯性会跑一遍这个标准流程:

dart format .        # 格式化代码
dart analyze         # 静态检查
dart test            # 运行测试
dart pub publish --dry-run  # 模拟发布,检查配置是否完整

特别是 --dry-run,它能帮你发现有没有漏掉 LICENSECHANGELOG 或者 pubspec.yaml 配置错误。

3、长期维护:不仅仅是发布

发布到 pub.dev 只是开始。你会面临 Issue 反馈、Pull Request、版本更新以及对新版 Flutter 的适配。

关于 API 的演进: 当你确实需要重构一个旧的 API 时,不要直接删掉它。正确做法是先使用 @Deprecated 标记它,引导用户迁移:

@Deprecated('请使用 create() 方法代替 initialize()')
void initialize() {
  create();
}

等到下一个 MAJOR 版本,再彻底移除它。

五、最后

Package 的本质不是为了写代码,而是为了构建一个“产品”。你的用户是开发者,而开发者的体验(DX, Developer Experience)决定了你的 Package 能走多远。

如果你的 Package 能做到:解决问题清晰、API 简单好用、文档直观、测试完备,那么它就能在 pub.dev 的生态里扎下根来。

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~