前言
鑒于現(xiàn)階段Flutter技術(shù)棧還不是太成熟,在使用Flutter做移動端開發(fā)時我們經(jīng)常需要借助Native平臺的力量來補充Flutter在這方面的缺陷,前面兩章我們通過學(xué)習(xí)把Flutter項目打包成AAR集成到原生平 跟 Flutter與原生平臺交互掌握了Flutter與原生平臺交互的兩種方式,但是有些場景下,我們希望我們Flutter跟原生交互的代碼可以
一次開發(fā),多處使用,類似于庫文件一樣,可以給其他項目或者其他開發(fā)著使用,這就是我們本篇文章要介紹的主題Flutter插件開發(fā)以及插件如何引用到項目中
課程目標(biāo)
- 學(xué)會如何新建Flutter插件,并了解插件項目結(jié)構(gòu)
- 掌握如何把插件引入到現(xiàn)有項目中
1.新建Flutter插件項目
新建Flutter插件項目跟新建Flutter項目的步驟一樣,無非是在新建項目的時候選擇的工程類型略有不同。
1.1新建項目
1.2 選擇Flutter Plugin
之后跟正常新建Flutter Applicition的操作一樣,正常給項目起名字,選擇工程路徑等一些列的初始化配置一直next到插件項目初始化完畢。之后的操作讀者一看便知,也沒有什么需要特別注意的地方,我就不逐個貼圖了。
1.3 插件項目結(jié)構(gòu)
從下面插件工程項目結(jié)構(gòu)圖中我們可以看出,F(xiàn)lutter插件項目跟普通的Flutter項目結(jié)構(gòu)上幾乎一樣,但是多出了一個example目錄,讀者打開example目錄后,會發(fā)現(xiàn)這個example目錄下面其實就是一個完整的Flutter項目,沒錯這個example就是為了方便我們在開發(fā)插件方便我們調(diào)試開發(fā)的功能是否正常可用,沒問題的話就可以發(fā)布出去或者給其他項目正常使用了。
插件開發(fā)其實用到的知識點就是通過利用我們上節(jié)課中講的Flutter跟原生平臺交互的方式來完成的,讓Flutter借助Native的功能來完成某種操作,插件化只不過是把調(diào)用平臺操作的代碼模塊化,便于后期其他項目或者別人引入,讓代碼
一次開發(fā),多處使用,由于涉及到的知識點在上一篇文章中我們都已經(jīng)講過了,所以這里就不在細講插件里的功能代碼實現(xiàn)邏輯了,下面我們來簡單分析一下這次課程中用到的用Flutter插件工程。
先看效果圖:

在上圖的插件工程中我們實現(xiàn)了,獲取系統(tǒng)版本號跟一個簡單的計算器的功能。下面看一下在插件工程中具體配置。
在插件工程的android端的業(yè)務(wù)實現(xiàn)邏輯:
class FlutterCalcPlugin : MethodCallHandler {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "flutter_calc_plugin")
channel.setMethodCallHandler(FlutterCalcPlugin())
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else if (call.method == "getResult") {
var a = call.argument<Int>("a")
var b = call.argument<Int>("b")
result.success((a!! + b!!).toString())
} else {
result.notImplemented()
}
}
}
由于插件開發(fā)完成之后是要在flutter端使用的,換句話說是要給dart文件引用的,所以下面的dart文件中定義的方法聲明才是我們開發(fā)的插件對調(diào)用者提供的方法。如下我們定義了
getplatformVersion:獲取系統(tǒng)版本號
getResult(int a, int b):計算兩個數(shù)的和
而兩個方法又通過methodChannel與平臺交互,借助native端來完成某些具體邏輯,然后把執(zhí)行完成的結(jié)果返回給調(diào)用方。插件定義完成并且成功導(dǎo)入到我們的項目中之后,我們就可以在項目中導(dǎo)入相關(guān)類以及方法引用,正常去使用我們自己開發(fā)的組件了。
插件工程的flutter端代碼:
class FlutterCalcPlugin {
static const MethodChannel _channel =
const MethodChannel('flutter_calc_plugin');
static Future<String> getplatformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
/**
*計算兩個數(shù)的和
*/
static Future<String> getResult(int a, int b) async {
Map<String, dynamic> map = {"a": a, "b": b};
String result = await _channel.invokeMethod("getResult", map);
print(result+"----------aa--");
return result;
}
}
這里涉及到跟平臺交互的部分我沒有具體展開講解,因為在上一篇文章中我們已經(jīng)講過相關(guān)的知識點了,如果讀者在這里不太明白平臺交互相關(guān)的邏輯,建議先去讀一下上一篇文章Flutter入門進階之旅(十九)Flutter與原生平臺交互
上述插件完整代碼地址:https://github.com/xiedong11/flutter_calc_plugin.git
2.插件引入到現(xiàn)有項目中
把我們開發(fā)完成的插件項目導(dǎo)入到現(xiàn)有項目中使用,我們可以通過github倉庫引入,或者本地引入,當(dāng)然也可以把開發(fā)完成的插件工程上傳到flutter的dart packages上然后通過版本號用pubspec.ymal文件引入,上傳dart packages的配置相對麻煩,限于篇幅,這里我就先只介紹前兩種方式,讀者如果對上傳dart packages感興趣的話可以私下里找我交流或者我會在后續(xù)的博客單獨整理出一篇博文來具體講解。
2.1 本地引入
如圖,我把插件工程放在項目跟目錄下的plugin文件下,插件項目名我們自己可以自己隨便定義,我這里把它定義成flutter_calc_plugin,那在我們要引入插件的項目中yaml文件里我們通過插件名,加路徑的方式把插件導(dǎo)入之后就可以正常使用插件里的功能了。
#本地插件引入
flutter_calc_plugin:
path: plugin/flutter_calc_plugin
2.2通過github倉庫地址引入
通過github倉庫地址引入相對簡單一些,就不用把插件拷貝到本地了,只需要在工程的yaml文件中正確配置插件的地址就可以導(dǎo)入了,兩種方式配置完成之后都別忘了執(zhí)行flutter packages get讓工程依賴同步一下。
#從github上引入插件依賴
flutter_calc_plugin:
git:
url:
https://github.com/xiedong11/flutter_calc_plugin.git
這里順便說一下小細節(jié)吧,由于yaml文件對縮進格式要求特別嚴格,讀者在配置插件引用或者其他第三方的庫時,一定要注意縮進。
還有就是具體采用上述兩種插件的哪一種方式,這個沒有固定的答案完全看你個人喜好跟插件的需求吧,舉個例子,如果你的插件開發(fā)出來幾乎不需要修改,那筆者建議通過github或者上傳到dart packages的方式引用,這樣不僅讓你的工程結(jié)構(gòu)更新清晰而且項目也好管理,但是如果先階段你開發(fā)的插件還不太成熟,或者經(jīng)常需要改動的話,建議使用本地的方式導(dǎo)入,這樣修改后調(diào)試代碼也方便,而且也省去了頻繁上傳插件的版本到dart packages或者github上去。
還有一個場景就是,比如使用的插件不是自己開發(fā)的,而是從github或者dart packages上找的別人開發(fā)好的,但恰恰他開發(fā)的插件不能完全滿足你的業(yè)務(wù)需求,或者你需要在此插件的基礎(chǔ)上重新定制UI或者補充邏輯,那這個時候你也可以把別人開發(fā)好的插件下載到本地,然后通過本地的方式引入到你的項目中再去針對你的業(yè)務(wù)去修改這個插件,直到修改到你滿意為止,然后再通過本地方式導(dǎo)入到項目中。
在文章的最后,貼上一下,上述gif圖上示例的具體代碼實現(xiàn)供讀者參考:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_calc_plugin/flutter_calc_plugin.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String addResult = '';
TextEditingController _addNumber1Controller,_addNumber2Controller;
@override
void initState() {
super.initState();
_addNumber1Controller = TextEditingController();
_addNumber2Controller = TextEditingController();
}
Future<void> getAddResult() async {
int addNumber1= int.parse(_addNumber1Controller.value.text);
int addNumber2=int.parse(_addNumber2Controller.value.text);
String result = '';
try {
result = await FlutterCalcPlugin.getResult(addNumber2, addNumber1);
} on PlatformException {
result = '未知錯誤';
}
setState(() {
addResult = result;
});
}
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion = await FlutterCalcPlugin.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('插件示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MaterialButton(
color: Colors.amber,
child: Text("獲取系統(tǒng)版本"),
onPressed: () {
initPlatformState();
},
),
Text('當(dāng)前系統(tǒng)版本 : $_platformVersion\n'),
SizedBox(height: 30),
Text("加法計算器"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 80,
child: TextField(
controller: _addNumber1Controller,
keyboardType: TextInputType.number,
),
),
Text(" + ",style: TextStyle(fontSize: 26),),
SizedBox(
width: 80,
child: TextField(
controller: _addNumber2Controller,
keyboardType: TextInputType.number,
),
),
Text(" = ",style: TextStyle(fontSize: 26),),
],
),
SizedBox(height: 30),
MaterialButton(
color: Colors.amber,
child: Text("結(jié)果等于"),
onPressed: () {
getAddResult();
},
),
Text(addResult),
],
)),
),
);
}
}
最后本章節(jié)以及專欄的所有完整代碼如下,讀者如果還不太明白,可以下載代碼自己跑一篇項目,慢慢的琢磨下具體的實現(xiàn)細節(jié):
專欄代碼倉庫:https://github.com/xiedong11/flutter_app.git