Flutter Packages 的开发和提交

Package 介绍

Package introduction

通过使用 packages (的模式)可以创建易于共享的模块化代码。一个最基本的 package 由以下内容构成:

Packages enable the creation of modular code that can be shared easily. A minimal package consists of:

  • pubspec.yaml 文件:用于定义 package 名称、版本号、作者等其他信息的元数据文件。

    A pubspec.yaml file: A metadata file that declares the package name, version, author, etc.

  • 包含共享代码的 lib 目录,其中至少包含一个 <package-name>.dart 文件。

    A lib directory containing the public code in the package, minimally a single <package-name>.dart file.

Package 类别

Package types

Package 包含以下两种类别:

Packages can contain several kinds of content:

  • 纯 Dart 库:用 Dart 编写的传统 package,比如 path。其中一些可能包含 Flutter 的特定功能,因此依赖于 Flutter 框架,其使用范围仅限于 Flutter,比如 fluro

    Dart packages: General packages written in Dart, for example the path package. Some of these might contain Flutter specific functionality and thus have a dependency on the Flutter framework, restricting their use to Flutter only, for example the fluro package.

  • 原生插件:使用 Dart 编写的,按需使用 Java 或 Kotlin、ObjC 或 Swift 分别在 Android 和/或 iOS 平台实现的 package。一个具体的例子是 battery

    Plugin packages: A specialized Dart package which contains an API written in Dart code combined with a platform-specific implementation for Android (using Java or Kotlin), and/or for iOS (using ObjC or Swift). A concrete example is the battery plugin package.

开发纯 Dart 库的 packages

Developing Dart packages

第一步:创建 package

Step 1: Create the package

想要创建纯 Dart 库的 package,请使用带有 --template=package 标志的 flutter create 命令:

To create a Dart package, use the --template=package flag with flutter create:

$ flutter create --template=package hello

这将在 hello/ 目录下创建一个 package 项目,其中包含以下内容:

This creates a package project in the hello/ folder with the following specialized content:

  • lib/hello.dart
    The Dart code for the package.


package 的 Dart 实现代码。

  • test/hello_test.dart
    The unit tests for the package.


Package 的 单元测试

第二步:实现 package

Step 2: Implement the package

对于纯 Dart 库的 package,只要在 lib/<package name>.dart 文件中添加功能实现,或在 lib 目录中的多个文件中添加功能实现。

For pure Dart packages, simply add the functionality inside the main lib/<package name>.dart file, or in several files in the lib directory.

如果要对 package 进行测试,在 test 目录下添加 单元测试

To test the package, add unit tests in a test directory.

关于如何组织 package 内容的更多详细信息,请参考 Dart library package 文档。

For additional details on how to organize the package contents, see the Dart library package documentation.

开发原生插件类型的 packages

Developing plugin packages

如果想要开发一个调用特定平台 API 的 package,你需要开发一个原生插件 packgae。原生插件 packgae 是 Dart package 的特别版本,除了要实现 Dart package 要实现的内容,还需要按需使用 Java 或 Kotlin、ObjC 或 Swift 分别在 Android 和/或 iOS 平台实现,你可以使用 platform channel 中的 API 来实现特定平台的调用。

If you want to develop a package that calls into platform-specific APIs, you need to develop a plugin package. A plugin package is a specialized version of a Dart package, that in addition to the content described above also contains platform-specific implementations written for Android (Java or Kotlin code), for iOS (Objective-C or Swift code), or for both. The API is connected to the platform-specific implementation(s) using platform channels.

第一步:创建 package

Step 1: Create the package

想要创建原生插件 package,请使用带有 --template=plugin 标志的 flutter create 命令。

To create a plugin package, use the --template=plugin flag with flutter create.

使用 --org 选项,以反向域名表示法来指定你的组织。该值用于生成的 Android 及

Use the --org option to specify your organization, using reverse domain name notation. This value is used in various package and bundle identifiers in the generated Android and iOS code.

$ flutter create --org com.example --template=plugin hello

这将在 hello/ 目录下创建一个插件项目,其中包含以下内容:

This creates a plugin project in the hello/ folder with the following specialized content:

  • lib/hello.dart:
    • Dart 插件 API 实现。

      The Dart API for the plugin.

  • android/src/main/java/com/example/​hello/HelloPlugin.kt:
    • Android 平台原生插件 API 实现。

      The Android platform specific implementation of the plugin API.

  • ios/Classes/HelloPlugin.m:
    • iOS 平台原生插件 API 实现。

      The iOS platform specific implementation of the plugin API.

  • example/:
    • 一个依赖于该插件并说明了如何使用它的 Flutter 应用。

      A Flutter app that depends on the plugin, and illustrates how to use it.

默认情况下,插件项目中 iOS 代码使用 Swift 编写,Android 代码使用 Kotlin 编写。如果你更喜欢 Objective-C 或 Java,你可以通过 -i 指定 iOS 所使用的语言和/或使用-a 指定 Android 所使用的语言。比如:

By default, the plugin project uses Swift for iOS code and Kotlin for Android code. If you prefer Objective-C or Java, you can specify the iOS language using -i and/or the Android language using -a. For example:

$ flutter create --template=plugin -i objc -a java hello

第二步:实现 package

Step 2: Implement the package

由于原生插件类型的 package 包含了使用多种编程语言编写的多个平台代码,因此需要一些特定步骤来保证体验的流畅性。

As a plugin package contains code for several platforms written in several programming languages, some specific steps are needed to ensure a smooth experience.

步骤 2a:定义 package API(.dart)

Step 2a: Define the package API (.dart)

原生插件类型 package 的 API 在 Dart 代码中要首先定义好,使用你钟爱的 Flutter 编辑器,打开 hello/ 主目录,并找到 lib/hello.dart 文件。

The API of the plugin package is defined in Dart code. Open the main hello/ folder in your favorite Flutter editor. Locate the file lib/hello.dart.

步骤 2b:添加 Android 平台代码(.java/.kt)

Step 2b: Add Android platform code (.java/.kt)

我们建议你使用 Android Studio 来编辑 Android 代码。

We recommend you edit the Android code using Android Studio.

使用 Android Studio 编辑 Android 平台代码之前,首先确保代码至少被构建过一次(换句话说,即从 IDE/编辑器执行示例程序,或在终端中执行以下命令:cd hello/example; flutter build apk)。

Before editing the Android platform code in Android Studio, first make sure that the code has been built at least once (in other words, run the example app from your IDE/editor, or in a terminal execute cd hello/example; flutter build apk).

接下来,

Next,

  1. 启动 Android Studio

    Launch Android Studio

  2. 在“Welcome to Android Studio”对话框中选择“Import project”,或在菜单中选择“File > New > Import Project…”,然后选择 hello/example/android/build.gradle 文件;

    Select ‘Import project’ in ‘Welcome to Android Studio’ dialog, or select ‘File > New > Import Project…’’ in the menu, and select the hello/example/android/build.gradle file.

  3. 在“Gradle Sync”对话框中,选择“OK”;

    In the ‘Gradle Sync’ dialog, select ‘OK’.

  4. 在“Android Gradle Plugin Update”对话框中,选择“Don’t remind me again for this project”。

    In the ‘Android Gradle Plugin Update’ dialog, select ‘Don’t remind me again for this project’.

插件的 Android 平台代码位于 hello/java/com.example.hello/​HelloPlugin

The Android platform code of your plugin is located in hello/java/com.example.hello/​HelloPlugin.

你可以在 Android Studio 中点击 ▶ 按钮来运行示例程序。

You can run the example app from Android Studio by pressing the ▶ button.

步骤 2c:添加 iOS 平台代码(.h+.m/.swift)

Step 2c: Add iOS platform code (.h+.m/.swift)

我们建议你使用 Xcode 来编辑 iOS 代码。

We recommend you edit the iOS code using Xcode.

使用 Xcode 编辑 iOS 平台代码之前,首先确保代码至少被构建过一次(即从 IDE/编辑器执行示例程序,或在终端中执行以下命令:cd hello/example; flutter build ios --no-codesign)。

Before editing the iOS platform code in Xcode, first make sure that the code has been built at least once (i.e., run the example app from your IDE/editor, or in a terminal execute cd hello/example; flutter build ios --no-codesign).

下一步,

Next,

  1. 启动 Xcode

    Launch Xcode

  2. 选择“File > Open”,然后选择 hello/example/ios/Runner.xcworkspace 文件。

    Select ‘File > Open’, and select the hello/example/ios/Runner.xcworkspace file.

插件的 iOS 平台代码位于项目导航中的 Pods/Development Pods/hello/Classes/

The iOS platform code of your plugin is located in Pods/Development Pods/hello/Classes/ in the Project Navigator.

你可以点击 ▶ 按钮来运行示例程序。

You can run the example app by pressing the ▶ button.

步骤 2d:关联 API 和平台代码

Step 2d: Connect the API and the platform code

最后,你需要将 Dart 编写的 API 代码与特定平台的实现相互关联。这是通过 platform channels 完成的。

Finally, you need to connect the API written in Dart code with the platform-specific implementations. This is done using platform channels.

添加文档

Adding documentation

建议将下列文档添加到所有 package 中:

It is recommended practice to add the following documentation to all packages:

  1. README.md 文件用来对 package 进行介绍

    A README.md file that introduces the package

  2. CHANGELOG.md 文件用来记录每个版本的更改

    A CHANGELOG.md file that documents changes in each version

  3. LICENSE 文件用来阐述 package 的许可条款

    A LICENSE file containing the terms under which the package is licensed

  4. API 文档包含所有的公共 API(详情参见下文)

    API documentation for all public APIs (see below for details)

API 文档

API documentation

当你提交一个 package 时,会自动生成 API 文档并将其提交到 dartdocs.org,示例请参见 device_info docs

When you publish a package, API documentation is automatically generated and published to dartdocs.org, see for example the device_info docs.

如果你希望在本地开发环境中生成 API 文档,可以使用以下命令:

If you wish to generate API documentation locally on your development machine, use the following commands:

  1. 将当前工作目录切换到 package 所在目录:

    Change directory to the location of your package:

    cd ~/dev/mypackage

  2. 告知文档工具 Flutter SDK 所在位置(更改以反应它所在的位置):

    Tell the documentation tool where the Flutter SDK is (change to reflect where you placed it):

    export FLUTTER_ROOT=~/dev/flutter(适用于 macOS 或 Linux 操作系统)

    export FLUTTER_ROOT=~/dev/flutter (on macOS or Linux)

    set FLUTTER_ROOT=~/dev/flutter(适用于 Windows 操作系统)

    set FLUTTER_ROOT=~/dev/flutter (on Windows)

  3. 运行 dartdoc 工具(作为 Flutter SDK 的一部分):

    Run the dartdoc tool (comes as part of the Flutter SDK):

    $FLUTTER_ROOT/bin/cache/dart-sdk/bin/dartdoc(适用于 macOS 或 Linux 操作系统)

    $FLUTTER_ROOT/bin/cache/dart-sdk/bin/dartdoc (on macOS or Linux)

    %FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dartdoc(适用于 Windows 操作系统)

    %FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dartdoc (on Windows)

关于如何编写 API 文档的建议,请参阅 [高效 Dart 指南][Effective Dart: Documentation]。

For tips on how to write API documentation, see [Effective Dart: Documentation][].

将许可证添加到 LICENSE 文件中

Adding licenses to the LICENSE file

每个 LICENSE 文件中的各个许可证应由 80 个短线字符组成的线段进行分割。

Individual licenses inside each LICENSE file should be separated by 80 hyphens on their own on a line.

如果 LICENSE 文件中包含多个组件许可证,那么每个组件许可证必须以其所在 package 的名称开始,每个 package 名称单独一行显示,并且 package 名称列表与实际许可证内容由空行隔开。(package 名称无需与 pub package 相匹配。比如,一个 package 可能包含多个第三方代码,并且可能需要为每个 package 添加许可证。)

If a LICENSE file contains more than one component license, then each component license must start with the names of the packages to which the component license applies, with each package name on its own line, and the list of package names separated from the actual license text by a blank line. (The packages need not match the names of the pub package. For example, a package might itself contain code from multiple third-party sources, and might need to include a license for each one.)

正确:

Good:

package_1

<some license text>

--------------------------------------------------------------------------------
package_2

<some license text>

正确:

Also good:

package_1

<some license text>

--------------------------------------------------------------------------------
package_1
package_2

<some license text>

不正确:

Bad:

<some license text>

--------------------------------------------------------------------------------
<some license text>

不正确:

Also bad:

package_1

<some license text>
--------------------------------------------------------------------------------
<some license text>

提交 package

Publishing packages

一旦完成了 package 的实现,你便可以将其提交到 pub.dev 上,以便其他开发者可以轻松地使用它。

Once you have implemented a package, you can publish it on the pub.dev, so that other developers can easily use it.

发布你的 package 之前,确保检查了这几个文件:pubspec.yamlREADME.mdCHANGELOG.md,确保它们完整且争取,另外,为了提高 package 的可用性,可以考虑加入如下的内容:

Prior to publishing, make sure to review the pubspec.yaml, README.md, and CHANGELOG.md files to make sure their content is complete and correct. Also, to improve the quality and usability of your package, consider including the items below.

  • 代码的示例用法

    Diverse code usage examples

  • 屏幕截图,GIF 动画或者视频

    Screenshots, animated gifs, or videos

  • 代码库的正确指向链接

    A link to the corresponding code repository

提交之前,请确保 pubspec.yamlREADME.md 以及 CHANGELOG.md 文件已被审查,以保证其内容的完整性和正确性。

接下来,运行 dry-run 命令以检验是否所有内容都通过了分析:

Next, run the dry-run command to see if everything passes analysis:

$ flutter pub publish --dry-run

最后,运行以下提交命令:

Finally, run the actual publish command:

$ flutter pub publish

有关提交的详细信息,请查阅关于 Pub 站点的 提交文档

For details on publishing, see the publishing docs for pub.dev.

Package 依赖处理

Handling package interdependencies

如果你正在开发的 hello 依赖于另外一个 package 所公开的 Dart API,你需要将该 package 添加到文件 pubspec.yamldependencies 段中。以下代码使得插件 url_launcher 的 Dart API 在 hello 中可用:

If you are developing a package hello that depends on the Dart API exposed by another package, you need to add that package to the dependencies section of your pubspec.yaml file. The code below makes the Dart API of the url_launcher plugin available to hello:

hello/pubspec.yaml 文件中:

In hello/pubspec.yaml:

dependencies:
  url_launcher: ^0.4.2

现在你可以在 hello 的 Dart 代码中使用 import 'package:url_launcher/url_launcher.dart'launch(someUrl)

You can now import 'package:url_launcher/url_launcher.dart' and launch(someUrl) in the Dart code of hello.

这与你在 Flutter 应用或其他任何 Dart 项目中引入 package 的方式没什么区别。

This is no different from how you include packages in Flutter apps or any other Dart project.

但碰巧 hello 是一个 原生插件 package,其特定的平台代码如果需要访问 url_launcher 所公开的平台特定 API,那么还需要为特定平台的构建文件添加适当的依赖说明,如下所示:

But if hello happens to be a plugin package whose platform-specific code needs access to the platform-specific APIs exposed by url_launcher, you also need to add suitable dependency declarations to your platform-specific build files, as shown below.

Android

hello/android/build.gradle 文件中:

In hello/android/build.gradle:

android {
    // lines skipped
    dependencies {
        provided rootProject.findProject(":url_launcher")
    }
}

现在你可以在 hello/android/src 目录下的源代码文件中使用 import io.flutter.plugins.urllauncher.UrlLauncherPlugin 并访问类 UrlLauncherPlugin

You can now import io.flutter.plugins.urllauncher.UrlLauncherPlugin and access the UrlLauncherPlugin class in the source code at hello/android/src.

iOS

hello/ios/hello.podspec 文件中:

In hello/ios/hello.podspec:

Pod::Spec.new do |s|
  # lines skipped
  s.dependency 'url_launcher'

现在你可以在 hello/ios/Classes 目录下的源代码文件中使用 #import "UrlLauncherPlugin.h" 并访问 UrlLauncherPlugin 这个类了。

You can now #import "UrlLauncherPlugin.h" and access the UrlLauncherPlugin class in the source code at hello/ios/Classes.