# Package
使用package可以創建共享的模塊化代碼。一個最小的package包括:
- 一個`pubspec.yaml`文件:聲明了package的名稱、版本、作者等的元數據文件。
- 一個 `lib` 文件夾:包括包中公開的(public)代碼,最少應有一個`<package-name>.dart`文件
Flutter Packages分為兩類:
- Dart包:其中一些可能包含Flutter的特定功能,因此對Flutter框架具有依賴性,這種包僅用于Flutter,例如[`fluro`](https://pub.dartlang.org/packages/fluro)包。
- 插件包:一種專用的Dart包,其中包含用Dart代碼編寫的API,以及針對Android(使用Java或Kotlin)和針對iOS(使用OC或Swift)平臺的特定實現,也就是說插件包括原生代碼,一個具體的例子是[`battery`](https://pub.dartlang.org/packages/battery)插件包。
注意,雖然Flutter的Dart運行時和Dart VM運行時不是完全相同,但是如果Package中沒有涉及這些存在差異的部分,那么這樣的包可以同時支持Flutter和Dart VM,如Dart http網絡庫[dio](https://github.com/flutterchina/dio).
## 開發Dart包
### 第一步:創建Dart包
您可以通過Android Studio:File>New>New Flutter Project 來創建:

您也可以通過使用`--template=package` 來執行 `flutter create` 命令來創建:
```
flutter create --template=package hello
```
這將在`hello/`文件夾下創建一個具有以下專用內容的package工程:
- `lib/hello.dart`:Package的Dart代碼
- `test/hello_test.dart`:Package的單元測試代碼。
### 實現package
對于純Dart包,只需在主`lib/<package name>.dart`文件內或`lib`目錄中的文件中添加功能即可 。要測試軟件包,請在`test`目錄中添加[unit tests](https://flutter.io/testing/#unit-testing)。下面我們看看如何組織Package包的代碼,我們以shelf Package為例,它的目錄結構如下:

在lib根目錄下的shelf.dart中,導出了多個lib/src目錄下的dart文件:
```
export 'src/cascade.dart';
export 'src/handler.dart';
export 'src/handlers/logger.dart';
export 'src/hijack_exception.dart';
export 'src/middleware.dart';
export 'src/pipeline.dart';
export 'src/request.dart';
export 'src/response.dart';
export 'src/server.dart';
export 'src/server_handler.dart';
```
而Package中主要的功能的源碼都在src目錄下。shelf Package也導出了一個迷你庫: shelf\_io,它主要是處理HttpRequest的。
**導入包**
當需要使用這個Package時,我們可以通過"package:"指令來指定包的入口文件:
```
import 'package:utilities/utilities.dart';
```
同一個包中的源碼文件之間也可以使用相對路徑來導入。
### 生成文檔
可以使用 [dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 工具來為Package生成文檔,開發者需要做的就是遵守文檔注釋語法在代碼中添加文檔注釋,最后使用dartdoc可以直接生成API文檔(一個靜態網站)。文檔注釋是使用三斜線"///"開始,如:
```
/// The event handler responsible for updating the badge in the UI.
void updateBadge() {
...
}
```
詳細的文檔語法請參考[dartdoc](https://github.com/dart-lang/dartdoc#dartdoc) 。
### 處理包的相互依賴
如果我們正在開發一個`hello`包,它依賴于另一個包,則需要將該依賴包添加到`pubspec.yaml`文件的`dependencies`部分。 下面的代碼使`url_launcher`插件的API在`hello`包中是可用的:
在 `hello/pubspec.yaml`中:
```
dependencies:
url_launcher: ^0.4.2
```
現在可以在`hello`中`import 'package:url_launcher/url_launcher.dart'` 然后調用 `launch()`方法了。
這與在Flutter應用程序或任何其他Dart項目中引用軟件包沒有什么不同。
但是,如果`hello`碰巧是一個插件包,其平臺特定的代碼需要訪問`url_launcher`公開的特定于平臺的API,那么我們還需要為特定于平臺的構建文件添加合適的依賴聲明,如下所示。
**Android**
在 `hello/android/build.gradle`:
```
android {
// lines skipped
dependencies {
provided rootProject.findProject(":url_launcher")
}
}
```
您現在可以在`hello/android/src`源碼中`import io.flutter.plugins.urllauncher.UrlLauncherPlugin`訪問`UrlLauncherPlugin`類。
**iOS**
在`hello/ios/hello.podspec`:
```
Pod::Spec.new do |s|
# lines skipped
s.dependency 'url_launcher'
```
您現在可以在`hello/ios/Classes`源碼中 `#import "UrlLauncherPlugin.h"` 然后訪問 `UrlLauncherPlugin`類。
### 解決依賴沖突
假設我們想在我們的`hello`包中使用`some_package`和`other_package`,并且這兩個包都依賴`url_launcher`,但是依賴的是`url_launcher`的不同的版本。 那我們就有潛在的沖突。避免這種情況的最好方法是在指定依賴關系時,程序包作者使用[版本范圍](https://www.dartlang.org/tools/pub/dependencies#version-constraints)而不是特定版本。
```
dependencies:
url_launcher: ^0.4.2 # 這樣會較好, 任何0.4.x(x >= 2)都可.
image_picker: '0.1.1' # 不是很好,只有0.1.1版本.
```
如果`some_package`聲明了上面的依賴關系,`other_package`聲明了`url_launcher`版本像’0.4.5’或’^0.4.0’,pub將能夠自動解決問題。
即使`some_package`和`other_package`聲明了不兼容的`url_launcher`版本,它仍然可能會和`url_launcher`以兼容的方式正常工作。 你可以通過向`hello`包的`pubspec.yaml`文件中添加依賴性覆蓋聲明來處理沖突,從而強制使用特定版本:
強制使用 `0.4.3`版本的`url_launcher`,在 `hello/pubspec.yaml`中:
```
dependencies:
some_package:
other_package:
dependency_overrides:
url_launcher: '0.4.3'
```
如果沖突的依賴不是一個包,而是一個特定于Android的庫,比如`guava`,那么必須將依賴重寫聲明添加到Gradle構建邏輯中。
強制使用`23.0`版本的`guava`庫,在`hello/android/build.gradle`中:
```
configurations.all {
resolutionStrategy {
force 'com.google.guava:guava:23.0-android'
}
}
```
Cocoapods目前不提供依賴覆蓋功能。
### 發布Package
一旦實現了一個包,我們可以在[Pub](https://pub.dartlang.org/)上發布它 ,這樣其他開發者就可以輕松使用它。
在發布之前,檢查`pubspec.yaml`、`README.md`以及`CHANGELOG.md`文件,以確保其內容的完整性和正確性。然后,運行 dry-run 命令以查看是否都準備OK了:
```
flutter packages pub publish --dry-run
```
驗證無誤后,我們就可以運行發布命令了:
```
flutter packages pub publish
```
> 如果你遇到包發布失敗的情況,先檢查是否因為眾所周知的網絡原因,如果是網絡問題,可以使用VPN,這里需要注意的是一些代理只會代理部分APP的網絡請求,如瀏覽器的,它們可能并不能代理dart的網絡請求,所以在這種情況下,即使開了代理也依然無法連接到Pub,因此,在發布Pub包時使用全局代理或全局VPN會保險些。如果網絡沒有問題,以管理員權限(sudo)運行發布命令重試。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示