創(chuàng)建Flutter插件工程
在Android Studio
里點(diǎn)擊Flie
- New
- New Flutter Project
,在左側(cè)里選中Flutter
,然后點(diǎn)擊Next
。
- 在
Project Name
里輸入項(xiàng)目名,只能是小寫英文 - 在
Project type
里選擇Plugin
- 在
Organization
里寫包名,.Project Name
會拼在包名的最后面成為包名的一部分
也可以使用命令行
flutter create --org com.example --template=plugin plugin_name
來創(chuàng)建插件,其中com.example
就是Organization
,plugin_name
就是Project Name
點(diǎn)擊Finish
后就成功創(chuàng)建一個(gè)插件工程了。
創(chuàng)建成功后可能默認(rèn)打開的是Android
工程,點(diǎn)擊切換為Project
。
切換后可以看到很多文件夾,我們需要關(guān)注的主要有以下4個(gè):
-
android
目錄是用來開發(fā)Android端的插件功能 -
ios
目錄是用來開發(fā)iOS端的插件功能 -
lib
是實(shí)現(xiàn)Flutter插件接口的代碼 -
example
目錄是測試項(xiàng)目,用來測試開發(fā)出來的插件的
打開iOS工程
ios
目錄只是一些零散的文件,是沒有工程的,所以我們怎么打開它來編寫呢???
我們注意到,在example
里也有一個(gè)iOS工程,沒錯(cuò),這個(gè)才是真正的工程!!!
但是我們打開發(fā)現(xiàn)會報(bào)錯(cuò),也沒有插件的文件,那怎么辦呢?
在打開工程之前,我們需要在Android Studio
里的命令行執(zhí)行以下命令:
cd example
flutter run
等執(zhí)行完成之后,我們就可以打開Runner.xcworkspace
文件了,這個(gè)時(shí)候我們發(fā)現(xiàn),多了一個(gè)Pods
工程。這個(gè)工程其實(shí)就是插件工程。
我們在Pods
工程的Development Pods
目錄下,找到Project Name
的文件夾,一直展開,最后就看到插件的文件了,我們就是在這個(gè)文件下編寫代碼。
編寫插件代碼
本文采用的語言是Swift
。
找到Swift項(xiàng)目名Plugin.swift
這個(gè)文件,該文件就是插件的實(shí)現(xiàn)文件。
在register
方法里,我們注冊了一個(gè)通道(已經(jīng)默認(rèn)注冊了),通道名默認(rèn)就是項(xiàng)目名,該名字在通信里必須是唯一的,可以修改,一旦修改,需要把dart
和android
里的名字也一并修改。
在handle
方法里,實(shí)現(xiàn)Flutter
調(diào)用原生的API,其中call.method
就是方法名,call.arguments
就是Flutter
傳遞過來的參數(shù)。使用result(返回值)
可以把結(jié)果返回給Flutter
。
當(dāng)找不到方法名時(shí),可以返回FlutterMethodNotImplemented
給Flutter
表示該方法還沒實(shí)現(xiàn),以此來做版本兼容。
具體實(shí)現(xiàn)如下:
public class SwiftNakiriPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "nakiri", binaryMessenger: registrar.messenger())
let instance = SwiftNakiriPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "stateString" { // 獲取網(wǎng)絡(luò)狀態(tài)的實(shí)現(xiàn)
result(ZTNetworkStateManager.shared.stateString)
} else if call.method == "bonusPoints" { // 使用參數(shù)的實(shí)現(xiàn)
let array = call.arguments as! Array<Int>
result(array[0] + array[1])
} else if call.method == "getPlatformVersion" { // 默認(rèn)的實(shí)現(xiàn)
result("iOS " + UIDevice.current.systemVersion)
} else {
// 找不到方法
result(FlutterMethodNotImplemented)
}
}
}
Objective-C
的插件文件名是項(xiàng)目名Plugin.m
,注冊方法是registerWithRegistrar
,實(shí)現(xiàn)插件內(nèi)容的方法是handleMethodCall
使用第三方庫
寫插件不可避免的會用到第三方庫,在使用第三方庫的時(shí)候,會遇到3種情況:
- 僅原生端使用第三方庫
- 僅
Flutter
端使用第三方庫 - 都使用同一個(gè)第三方庫
不同的情況有不同的處理方式。
僅原生端使用第三方庫
當(dāng)僅原生端需要依賴某些第三方庫時(shí),可以在項(xiàng)目名.podspec
文件里加上s.dependency '第三方庫名'
,如:
s.dependency 'Alamofire'
然后打開命令行,跳轉(zhuǎn)到Runner.xcworkspace
所在的目錄,然后pod install
即可。
僅Flutter端使用第三方庫
當(dāng)僅Flutter
端需要依賴某些第三方庫時(shí),可以在pubspec.yaml
文件里的dependencies
部分,如:
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.0.16
之后在Android Studio
里執(zhí)行Pub get
就行了。
都使用同一個(gè)第三方庫
假設(shè)Flutter
里需要用到url_launcher
,然后原生里也需要用到,那我們就得在Flutter
的pubspec.yaml
文件里的dependencies
部分添加依賴包,同時(shí)也要在iOS
端的項(xiàng)目名.podspec
文件里加上s.dependency 'url_launcher'
。
Flutter端實(shí)現(xiàn)
剛才我們已經(jīng)在插件里增加了一個(gè)名字叫stateString
的方法,但是Flutter
端還沒實(shí)現(xiàn),我們現(xiàn)在去把它實(shí)現(xiàn)。
找到lib
文件夾下的項(xiàng)目名.dart
文件,里面就有一個(gè)類,類名就是項(xiàng)目名,我們增加一個(gè)方法用來調(diào)用iOS
端的stateString
方法,方法名不需要和iOS
端的保持一致,主要是通道里調(diào)用iOS
端的方法名就行了,代碼如下:
class Nakiri {
static const MethodChannel _channel = MethodChannel('nakiri'); /// 通道名,需和iOS、android端保持一致
/// 默認(rèn)實(shí)現(xiàn)
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
/// 實(shí)現(xiàn)iOS端新增的方法
static Future<String> stateString() async {
final String state = await _channel.invokeMethod('stateString');
return state;
}
/// 實(shí)現(xiàn)iOS端新增的方法
static Future<int> add() async {
final int result = await _channel.invokeMethod('bonusPoints', [5, 8]); /// 接收一個(gè)數(shù)組或者字典作為參數(shù)傳遞給原生端
return result;
}
}
Flutter端測試的實(shí)現(xiàn)
在example
目錄里的lib
目錄,里面有一個(gè)main.dart
文件,該文件就是測試使用的文件,我們在它本來的實(shí)現(xiàn)上修改一下,代碼如下:
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String _stateString = 'Unknown';
int _add = -2;
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
String platformVersion;
String stateString;
int add;
try {
platformVersion =
await Nakiri.platformVersion ?? 'Unknown platform version';
stateString =
await Nakiri.stateString();
add =
await Nakiri.add();
} on PlatformException {
platformVersion = 'Failed to get platform version.';
stateString = 'Failed to get stateString';
add = -1;
}
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
_stateString = stateString;
_add = add;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text('Running on: $_platformVersion\n'),
),
Center(
child: Text('Network is: $_stateString\n'),
),
Center(
child: Text('Bonus points is: $_add\n'),
),
],
),
),
);
}
}
需要注意的是,
Flutter
和原生通信都是異步的,所以都需要使用await
和async
通信的數(shù)據(jù)類型
原生與Flutter
互相通信時(shí)使用的數(shù)據(jù)類型是有限制的,以下是可用的數(shù)據(jù)類型:
Dart | kotlin | Java | Objective-C | Swift |
---|---|---|---|---|
null | null | null | NSNull | NSNull |
bool | Boolean | java.lang.Boolean | NSNumber numberWithBool: | NSNumber(value: Bool)或者Bool |
int 32位平臺 | Int | java.lang.Integer | NSNumber numberWithInt: | NSNumber(value: Int32)或者Int32 |
int | Long | java.lang.Long | NSNumber numberWithLong: | NSNumber(value: Int)或者Int |
double | Double | java.lang.Double | NSNumber numberWithDouble: | NSNumber(value: Double)或者Double |
String | String | java.lang.String | NSString | String或者NSString |
Uint8List | ByteArray | byte[] | FlutterStandardTypedData typedDataWithBytes: | FlutterStandardTypedData(bytes: Data) |
Int32List | IntArray | int[] | FlutterStandardTypedData typedDataWithInt32: | FlutterStandardTypedData(int32: Data) |
Int64List | LongArray | long[] | FlutterStandardTypedData typedDataWithInt64: | FlutterStandardTypedData(int64: Data) |
Float32List | FloatArray | float[] | FlutterStandardTypedData typedDataWithFloat32: | FlutterStandardTypedData(float32: Data) |
Float64List | DoubleArray | double[] | FlutterStandardTypedData typedDataWithFloat64: | FlutterStandardTypedData(float64: Data) |
List | List | java.util.ArrayList | NSArray | Array或者NSArray |
Map | HashMap | java.util.HashMap | NSDictionary | Dictionary或者NSDictionary |
- 從表里可知,
Swift
的基礎(chǔ)類型可以用Objective-C
的對象類型,集合類型可以兼容Objective-C
的集合類型(不過這些都是Swift
本身的特性)- 在使用
Swift
時(shí),最好還是使用它本身的類型,如果使用Objective-C
的類型,就無法判斷詳細(xì)類型,比如Int
和Double
,在使用Objective-C
類型的時(shí)候,都是NSNumber
原生與Flutter通信
上面都是講Flutter
怎么調(diào)原生的,那原生能不能主動去調(diào)Flutter
呢?
我們先看看Flutter
提供的3個(gè)通信類:
-
FlutterMethodChannel
用于方法調(diào)用 -
FlutterBasicMessageChannel
用于傳遞簡單數(shù)據(jù) -
FlutterEventChannel
用于監(jiān)聽數(shù)據(jù)流
每種Channel
均有三個(gè)重要成員變量:
-
name
:String類型,代表Channel的名字,也是其唯一標(biāo)識符 -
messager
:BinaryMessenger類型,代表消息信使,是消息的發(fā)送與接收的工具 -
codec
: MessageCodec類型或MethodCodec類型,代表消息的編解碼器
除了需要自定義
name
之外,其余變量用默認(rèn)值即可
前兩種Channel
都提供了原生和Flutter
互相通信的能力,而FlutterEventChannel
不支持Flutter
端發(fā)送數(shù)據(jù),由此可見,它們的應(yīng)用場合不太一樣,接下來我會講解它們每個(gè)的使用方法。
所有的
Channel
都需要名字,在一個(gè)項(xiàng)目中可能會有很多的Channel
,每個(gè)Channel
都應(yīng)該使用唯一的命名標(biāo)識,否則可能會被覆蓋。當(dāng)有消息從Flutter
端發(fā)送到原生端時(shí),會根據(jù)其傳遞過來的名字找到該Channel
對應(yīng)的Handler
(消息處理器)
推薦的命名方式是組織名稱加插件的名稱,例如:com.nakiri.ayame/native_image_view,如果一個(gè)插件中包含了多個(gè)Channel
可再根據(jù)功能模塊進(jìn)一步進(jìn)行區(qū)分
FlutterMethodChannel
通過前面我們已經(jīng)知道Flutter
是通過FlutterMethodChannel
去調(diào)原生方法的,接下來我們在原生端也使用FlutterMethodChannel
去調(diào)Flutter
的方法。
首先我們改造下原生端的插件類,在里面定義一個(gè)屬性,同時(shí)把register
方法注冊的FlutterMethodChannel
賦值給該屬性。
private static var methodChannel: FlutterMethodChannel!
methodChannel = FlutterMethodChannel(name: "nakiri", binaryMessenger: registrar.messenger())
然后我們就可以通過FlutterMethodChannel
的invokeMethod
方法調(diào)用Flutter
的方法了。
// 調(diào)用Flutter方法
SwiftNakiriPlugin.methodChannel.invokeMethod("updateNumber", arguments: (number + 1)) { value in
result(value) // 獲取Flutter方法的返回值,并返回給Flutter
}
然后在Flutter
端插件代碼中增加監(jiān)聽方法:
static int number = 1;
/// 當(dāng)需要原生調(diào)用Flutter方法時(shí),請先調(diào)用下初始化方法來增加監(jiān)聽
static void init() {
/// 設(shè)置原生調(diào)用Flutter時(shí)的回調(diào)
_channel.setMethodCallHandler((call) async {
switch(call.method) {
case "updateNumber":
return _updateNumber(call.arguments); /// 把結(jié)果返回給原生端
default:
break;
}
});
}
/// 實(shí)現(xiàn)原生調(diào)用Flutter方法
static int _updateNumber(int value) {
return number + value;
}
FlutterBasicMessageChannel
相對于FlutterMethodChannel
需要綁定代理,FlutterBasicMessageChannel
在處理消息上更為方便靈活,并且能發(fā)送大內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)。
Flutter端使用FlutterBasicMessageChannel發(fā)送數(shù)據(jù)給原生端
流程如下:
- 原生端創(chuàng)建
FlutterBasicMessageChannel
- 原生端使用
setMessageHandler
方法,設(shè)置該Channel
的MessageHandler
回調(diào) -
Flutter
端創(chuàng)建該name
的BasicMessageChannel
-
Flutter
端使用該BasicMessageChannel
通過send
方法向原生端發(fā)送消息 - 原生端的
MessageHandler
收到Flutter
端發(fā)送的消息,在閉包中獲取到值,并且可以通過reply
閉包給Flutter
端回復(fù) -
Flutter
端處理該回復(fù)
原生端的插件類增加的代碼:
private static var basicMessageChannel: FlutterBasicMessageChannel!
// 原生和Flutter互相發(fā)送數(shù)據(jù)
basicMessageChannel = FlutterBasicMessageChannel(name: "flutter_plugin_basic_nakiri", binaryMessenger: registrar.messenger())
// 設(shè)置消息接收器,用來接收數(shù)據(jù);當(dāng)Flutter端發(fā)送消息過來的時(shí)候,會自動回調(diào);設(shè)置為nil時(shí)取消監(jiān)聽
basicMessageChannel.setMessageHandler { value, reply in
reply(value as! Int + 1) // 使用reply給Flutter端回復(fù)消息
}
Flutter
端插件只需要增加一個(gè)屬性即可:
static const BasicMessageChannel basicMessageChannel = BasicMessageChannel("flutter_plugin_basic_nakiri", StandardMessageCodec()); /// 定義一個(gè)渠道事件監(jiān)聽;名字需要唯一且各端保持一致
然后在Flutter
測試工程里,對原生發(fā)送數(shù)據(jù):
Nakiri.basicMessageChannel.send(_basicNumber).then((value) {
setState(() {
_basicNumber = value;
});
});
原生端使用FlutterBasicMessageChannel發(fā)送數(shù)據(jù)給Flutter端
流程如下:
-
Flutter
端創(chuàng)建BasicMessageChannel
-
Flutter
端使用setMessageHandler
方法設(shè)置該Channel
的Handler
回調(diào) - 原生端創(chuàng)建該
name
的BasicMessageChannel
- 原生端使用該
BasicMessageChannel
通過sendMessage
方法向Flutter
端發(fā)送消息 -
Flutter
端的Handler
收到發(fā)送的消息,并處理消息,然后通過return
進(jìn)行回復(fù) - 原生端處理該回復(fù)
原生端的插件類增加的代碼:
// 給Flutter發(fā)送數(shù)據(jù),并等待Flutter端回復(fù)
SwiftNakiriPlugin.basicMessageChannel.sendMessage(number + 1) { value in
result(value)
}
Flutter
端插件增加的代碼:
/// 設(shè)置原生發(fā)送消息給Flutter時(shí)的回調(diào)
basicMessageChannel.setMessageHandler((message) async {
return message; /// 收到消息后,可以通過return把值回復(fù)給原生
});
FlutterEventChannel
由于FlutterEventChannel
只能原生給Flutter
發(fā)送消息,并且無返回值,所以它只能用來傳輸一些實(shí)時(shí)數(shù)據(jù)。
使用FlutterEventChannel
步驟如下:
-
Flutter
端定義一個(gè)EventChannel
-
Flutter
端使用receiveBroadcastStream
里的listen
監(jiān)聽該Channel
- 原生端定義一個(gè)
EventChannel
- 原生端通過
setStreamHandler
方法設(shè)置代理 - 原生端實(shí)現(xiàn)代理的
onListen
和onCancel
方法,并在onListen
里獲取eventSink
閉包,在onCancel
方法里釋放eventSink
閉包 - 原生端使用
eventSink
閉包方法消息 -
Flutter
端接收到消息并處理
原生端增加一個(gè)類,用來封裝相關(guān)邏輯:
class ZTEventChannel: NSObject {
private var eventChannel: FlutterEventChannel?
private var eventSink: FlutterEventSink?
private var timer: Timer?
private var number = 5
override init() {
super.init()
}
required convenience init(binaryMessenger messenger: FlutterBinaryMessenger) {
self.init()
eventChannel = FlutterEventChannel(name: "flutter_plugin_event_nakiri", binaryMessenger: messenger) // 通道名必須唯一且和各端保持一致
eventChannel?.setStreamHandler(self)
}
private func removeTimer() {
timer?.invalidate()
timer = nil
number = 5
}
private func createTimer() {
if #available(iOS 10.0, *) {
if timer == nil {
timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { [weak self] (timer) in
self?.timerCall()
})
}
}
}
@objc private func timerCall() {
if let event = eventSink {
event(number)
number += 5
}
}
}
extension ZTEventChannel: FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
// 在這里獲取到eventSink
self.eventSink = events
createTimer()
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
// 在這里移除eventSink
self.eventSink = nil
removeTimer()
return nil
}
}
然后在原生端插件類里使用該類:
// 原生實(shí)時(shí)發(fā)送數(shù)據(jù)流給Flutter
eventChannel = ZTEventChannel(binaryMessenger: registrar.messenger())
Flutter
端插件增加一個(gè)屬性:
static const EventChannel eventChannel = EventChannel("flutter_plugin_event_nakiri"); /// 定義一個(gè)渠道事件監(jiān)聽;名字需要唯一且各端保持一致
然后在別的地方可以使用該屬性來接收原生端發(fā)來的數(shù)據(jù)了:
_initStream() {
/// 監(jiān)聽原生發(fā)來的消息
_stream ??= Nakiri.eventChannel.receiveBroadcastStream().listen((data) {
/// 這里的data就是原生端發(fā)送過來的數(shù)據(jù)
setState(() {
_eventValue = data;
});
}, onError: (error) { /// 錯(cuò)誤處理
setState(() {
_eventValue = -5;
});
});
}
此外,還可以暫停接收數(shù)據(jù)、重新接收數(shù)據(jù)和移除接收數(shù)據(jù):
/// 暫停數(shù)據(jù)接收
_stream?.pause();
/// 恢復(fù)數(shù)據(jù)流
_stream?.resume();
_removeStream() {
if (_stream != null) {
/// 移除監(jiān)聽
_stream?.cancel();
_stream = null;
}
}
移除監(jiān)聽后,想要重新監(jiān)聽時(shí),只需要調(diào)用
_initStream()
方法即可,不需要重新創(chuàng)建eventChannel
的
插件上傳到Pub
上傳插件前,需要完善一些資料:
-
README.md
介紹包的文件 -
CHANGELOG.md
記錄每個(gè)版本中的更改 -
LICENSE
包含軟件包許可條款的文件 -
pubspec.yaml
的資料 - 所有公共API的API文檔
首先是pubspec.yaml
,對Flutter
插件來說,pubspec.yaml
里除了插件的依賴,還包含一些元信息,根據(jù)需要,把這些補(bǔ)上:
name: xxx # 要發(fā)布的項(xiàng)目名稱
description: xxxxxx. # 項(xiàng)目描述
version: 0.0.1 # 發(fā)布的版本
homepage: http://www.github.com/xxx # 項(xiàng)目主頁
issue_tracker: http://www.github.com/xxx # issue,一般寫當(dāng)前插件源代碼的Github issue地址
repository: http://www.github.com/xxx.git # 一般寫當(dāng)前插件源代碼的Github地址
另外,發(fā)布到Pub
上的包需要包含一個(gè)LICENSE
許可條款文件,不想麻煩的話,可以在GitHub
創(chuàng)建倉庫的時(shí)候選中一個(gè)。
發(fā)布前檢查
我們打開命令行,跳轉(zhuǎn)到pubspec.yaml
文件所在的目錄,在命令行使用以下命令來測試發(fā)布:
flutter packages pub publish --dry-run --server=https://pub.dartlang.org
之所以使用
--server
來指定服務(wù)器,是因?yàn)槲覀冊谂渲铆h(huán)境的時(shí)候,一般都配置了這2個(gè)變量:PUB_HOSTED_URL=https://pub.flutter-io.cn
和FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
,直接上傳會出現(xiàn)問題
如果沒有發(fā)現(xiàn)問題,如圖所示:
然后還需要做的就是上傳前的需要清理插件,避免插件過大無法上傳:
flutter clean
使用以下命令來發(fā)布插件:
flutter packages pub publish --server=https://pub.dartlang.org
因?yàn)槭前l(fā)布到谷歌的平臺,所以需要登錄谷歌賬號進(jìn)行認(rèn)證。
在我們輸入flutter packages pub publish
命令之后,我們會收到一條認(rèn)證鏈接,使用瀏覽器打開鏈接就可以驗(yàn)證了。
我們選擇自己的賬戶,即可開始驗(yàn)證,命令行會自行同步狀態(tài),無須我們自己處理的。
網(wǎng)頁出現(xiàn)以下提示,就說明驗(yàn)證成功。
之后我們只需要等待即可,命令行會自行上傳插件到Pub
的。
但是,如果遇到這種情況,說明是被墻了,需要使用代理。
特別說明下,在代理客戶端上開啟了代理,并不等于命令行就開啟了代理,命令行需要額外開啟,具體方法可自行查找。
上傳成功后,會出現(xiàn)如下提示:
上傳成功后,并不會馬上能看到,請耐心等待。
插件的使用方式
插件有4種使用方式:
- pub
- git
- 本地
- 私有pub庫
pub依賴
這種是最常見的方式,直接在工程的pubspec.yaml
中寫上你需要的插件名和版本,之后執(zhí)行Pub get
就行了。
dependencies:
flutter:
sdk: flutter
nakiri: ^0.0.1 # 添加庫
git依賴
如果我們不想發(fā)布到pub
,但又想團(tuán)隊(duì)共享插件,那么我們可以把庫上傳到git倉庫里面,然后在pubspec.yaml
中配置,之后執(zhí)行Pub get
就行了。
dependencies:
flutter:
sdk: flutter
nakiri:
git:
url: https://github.com/xxx/nakiri.git
ref: nakiri_fixes_issue_520
path: packages/nakiri_2
-
url
:git地址 -
ref
:表示git引用,可以是commit hash
,tag
或者分支
-
path
:如果git倉庫中有多個(gè)軟件包,則可以使用此屬性指定軟件包
本地依賴
上面的方法都需要上傳到服務(wù)器,較為麻煩,如果只是自己用或者調(diào)試插件,那么最好的方式就是本地依賴,只需要在pubspec.yaml
中配置路徑,之后執(zhí)行Pub get
就行了。
dependencies:
flutter:
sdk: flutter
nakiri:
path: ../xxx/nakiri/
path
可以是相對路徑,也可以是絕對路徑
私有pub倉庫依賴
一般而言,pub
管理插件比git
管理方便,所以一般大公司都會搭建自己的私有pub倉庫,依賴私有pub倉庫也很簡單,只需要在pubspec.yaml
中配置完成后,之后執(zhí)行Pub get
就行了。
dependencies:
flutter:
sdk: flutter
nakiri:
hosted:
name: nakiri
url: http://your-package-server.com
version: ^0.0.1
依賴覆蓋
當(dāng)2個(gè)以上的插件依賴于另一個(gè)插件,并且他們所依賴的版本不一致的時(shí)候,就可能出現(xiàn)版本沖突,要解決這個(gè)沖突,我們可以使用dependency_overrides
強(qiáng)制某個(gè)插件使用某個(gè)版本,如:
dependencies:
nakiri_2: ^1.0.1
nakiri: ^0.0.1
dependency_overrides:
url_launcher: ^5.4.0
這里假設(shè)nakiri_2
和nakiri
都依賴url_launcher
,但所依賴的版本不一樣,通過這種方式,可以讓它們都依賴成5.4.0
版本。
雖然這種方式可以解決依賴報(bào)錯(cuò),但可能會由于版本的改動使得API接口可能不一樣,最終還是可能會出問題,所以,慎用。
相關(guān)鏈接
整個(gè)項(xiàng)目已經(jīng)發(fā)到GitHub:demo地址
插件也已經(jīng)發(fā)布到Pub倉庫:插件地址
參考資料:Flutter中文網(wǎng) - Flutter插件教程
iOS OC Swift Flutter開發(fā)群 139322447