Flutter實(shí)戰(zhàn) 開(kāi)發(fā)Package

2021-03-09 10:13 更新

第二章中已經(jīng)講過(guò)如何使用 Package(包),我們知道通過(guò) package 可以創(chuàng)建共享的模塊化代碼,本節(jié)將重點(diǎn)講一下如何開(kāi)發(fā)并發(fā)布我們自己的 Package。一個(gè)最小的 Package 包括:

  • 一個(gè)pubspec.yaml文件:聲明了 Package 的名稱(chēng)、版本、作者等的元數(shù)據(jù)文件。
  • 一個(gè) lib 文件夾:包括包中公開(kāi)的(public)代碼,最少應(yīng)有一個(gè)<package-name>.dart文件

Flutter Packages 分為兩類(lèi):

  • Dart 包:其中一些可能包含 Flutter 的特定功能,因此對(duì) Flutter 框架具有依賴(lài)性,這種包僅用于Flutter,例如fluro (opens new window)包。
  • 插件包:一種專(zhuān)用的 Dart 包,其中包含用 Dart 代碼編寫(xiě)的 PI,以及針對(duì) Android(使用 Java 或 Kotlin)和針對(duì) iOS(使用 OC 或 Swift)平臺(tái)的特定實(shí)現(xiàn),也就是說(shuō)插件包括原生代碼,一個(gè)具體的例子是battery (opens new window)插件包。

注意,雖然 Flutter 的 Dart 運(yùn)行時(shí)和 Dart VM 運(yùn)行時(shí)不是完全相同,但是如果 Package 中沒(méi)有涉及這些存在差異的部分,那么這樣的包可以同時(shí)支持 Flutter 和 Dart VM,如 Dart http 網(wǎng)絡(luò)庫(kù)dio (opens new window)。

下面我將帶領(lǐng)讀者一步步來(lái)開(kāi)發(fā)一個(gè) Dart Package。

#第一步:創(chuàng)建Dart包

您可以通過(guò) Android Studio:File>New>New Flutter Project 來(lái)創(chuàng)建一個(gè) Package 工程,如圖12-1所示:

圖12-1

您也可以通過(guò)使用--template=package 來(lái)執(zhí)行 flutter create 命令來(lái)創(chuàng)建:

flutter create --template=package hello

這將在hello/文件夾下創(chuàng)建一個(gè)具有以下專(zhuān)用內(nèi)容的 package 工程:

  • lib/hello.dart:Package 的 Dart 代碼
  • test/hello_test.dart:Package 的單元測(cè)試代碼。

#實(shí)現(xiàn)package

對(duì)于純 Dart 包,只需在主lib/<package name>.dart文件內(nèi)或lib目錄中的文件中添加功能即可 。要測(cè)試軟件包,請(qǐng)?jiān)?code>test目錄中添加unit tests (opens new window)。下面我們看看如何組織 Package 包的代碼,我們以 shelf Package 為例,它的目錄結(jié)構(gòu)如圖12-2所示:

在 lib 根目錄下的“shelf.dart”中,導(dǎo)出了多個(gè)“l(fā)ib/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 也導(dǎo)出了一個(gè)迷你庫(kù): shelf_io,它主要是處理 HttpRequest 的。

#導(dǎo)入包

當(dāng)需要使用這個(gè) Package 時(shí),我們可以通過(guò)"package:"指令來(lái)指定包的入口文件:

import 'package:utilities/utilities.dart';

同一個(gè)包中的源碼文件之間也可以使用相對(duì)路徑來(lái)導(dǎo)入。

#生成文檔

可以使用 dartdoc (opens new window)工具來(lái)為 Package 生成文檔,開(kāi)發(fā)者需要做的就是遵守文檔注釋語(yǔ)法在代碼中添加文檔注釋?zhuān)詈笫褂?dartdoc 可以直接生成 API 文檔(一個(gè)靜態(tài)網(wǎng)站)。文檔注釋是使用三斜線(xiàn)"///"開(kāi)始,如:

/// The event handler responsible for updating the badge in the UI.
void updateBadge() {
  ...
}

詳細(xì)的文檔語(yǔ)法請(qǐng)參考dartdoc (opens new window)。

#處理包的相互依賴(lài)

如果我們正在開(kāi)發(fā)一個(gè)hello包,它依賴(lài)于另一個(gè)包,則需要將該依賴(lài)包添加到pubspec.yaml文件的dependencies部分。 下面的代碼使url_launcher插件的 API 在hello包中是可用的:

hello/pubspec.yaml中:

dependencies:
  url_launcher: ^0.4.2

現(xiàn)在可以在helloimport 'package:url_launcher/url_launcher.dart' 然后調(diào)用 launch()方法了。

這與在Flutter應(yīng)用程序或任何其他 Dart 項(xiàng)目中引用軟件包沒(méi)有什么不同。

但是,如果hello碰巧是一個(gè)插件包,其平臺(tái)特定的代碼需要訪(fǎng)問(wèn)url_launcher公開(kāi)的特定于平臺(tái)的 API,那么我們還需要為特定于平臺(tái)的構(gòu)建文件添加合適的依賴(lài)聲明,如下所示。

Android

hello/android/build.gradle:

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

您現(xiàn)在可以在hello/android/src源碼中import io.flutter.plugins.urllauncher.UrlLauncherPlugin訪(fǎng)問(wèn)UrlLauncherPlugin類(lèi)。

iOS

hello/ios/hello.podspec:

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

您現(xiàn)在可以在hello/ios/Classes源碼中 #import "UrlLauncherPlugin.h" 然后訪(fǎng)問(wèn) UrlLauncherPlugin類(lèi)。

#解決依賴(lài)沖突

假設(shè)我們想在我們的hello包中使用some_packageother_package,并且這兩個(gè)包都依賴(lài)url_launcher,但是依賴(lài)的是url_launcher的不同的版本。 那我們就有潛在的沖突。避免這種情況的最好方法是在指定依賴(lài)關(guān)系時(shí),程序包作者使用版本范圍 (opens new window)而不是特定版本。

dependencies:
  url_launcher: ^0.4.2    # 這樣會(huì)較好, 任何0.4.x(x >= 2)都可.
  image_picker: '0.1.1'   # 不是很好,只有0.1.1版本.

如果some_package聲明了上面的依賴(lài)關(guān)系,other_package聲明了url_launcher版本像’0.4.5’或’^0.4.0’,pub將能夠自動(dòng)解決問(wèn)題。

即使some_packageother_package聲明了不兼容的url_launcher版本,它仍然可能會(huì)和url_launcher以兼容的方式正常工作。 你可以通過(guò)向hello包的pubspec.yaml文件中添加依賴(lài)性覆蓋聲明來(lái)處理沖突,從而強(qiáng)制使用特定版本:

強(qiáng)制使用 0.4.3版本的url_launcher,在 hello/pubspec.yaml中:

dependencies:
  some_package:
  other_package:
dependency_overrides:
  url_launcher: '0.4.3'

如果沖突的依賴(lài)不是一個(gè)包,而是一個(gè)特定于 Android 的庫(kù),比如guava,那么必須將依賴(lài)重寫(xiě)聲明添加到 Gradle 構(gòu)建邏輯中。

強(qiáng)制使用23.0版本的guava庫(kù),在hello/android/build.gradle中:

configurations.all {
    resolutionStrategy {
        force 'com.google.guava:guava:23.0-android'
    }
}

Cocoapods 目前不提供依賴(lài)覆蓋功能。

#發(fā)布Package

一旦實(shí)現(xiàn)了一個(gè)包,我們可以在Pub (opens new window)上發(fā)布它 ,這樣其他開(kāi)發(fā)者就可以輕松使用它。

在發(fā)布之前,檢查pubspec.yaml、README.md以及CHANGELOG.md文件,以確保其內(nèi)容的完整性和正確性。然后,運(yùn)行 dry-run 命令以查看是否都準(zhǔn)備OK了:

flutter packages pub publish --dry-run

驗(yàn)證無(wú)誤后,我們就可以運(yùn)行發(fā)布命令了:

flutter packages pub publish

如果你遇到包發(fā)布失敗的情況,先檢查是否因?yàn)楸娝苤木W(wǎng)絡(luò)原因,如果是網(wǎng)絡(luò)問(wèn)題,可以使用 VPN,這里需要注意的是一些代理只會(huì)代理部分 APP 的網(wǎng)絡(luò)請(qǐng)求,如瀏覽器的,它們可能并不能代理 dart 的網(wǎng)絡(luò)請(qǐng)求,所以在這種情況下,即使開(kāi)了代理也依然無(wú)法連接到 Pub,因此,在發(fā)布 Pub 包時(shí)使用全局代理或全局 VPN 會(huì)保險(xiǎn)些。如果網(wǎng)絡(luò)沒(méi)有問(wèn)題,以管理員權(quán)限(sudo)運(yùn)行發(fā)布命令重試。
很多時(shí)候開(kāi)啟全局代理也不會(huì)讓 terminal 中的流量打代理服務(wù)器走,以 socks5 為例,應(yīng)該在終端下輸入以下指令:

export all_proxy=socks5://127.0.0.1:1080

此時(shí)終端中的 http 和 https 流量會(huì)打代理服務(wù)器走,可以通過(guò)curl -i https://ip.cn指令查看代理設(shè)置是否成功。

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)