相對于單獨開發Flutter應用,混合開發對于線上項目更具有實際意義,可以把風險控制到最低,也可以進行實戰上線。所以介紹 集成已有項目
混合開發涉及原生Native和Flutter進行通信傳輸,還有插件編寫,所以介紹 兩端通信Flutter Platform Channel的使用
集成已有項目
采用官方的實現方式,對個人和小團隊相對簡單易用,并沒有實踐閑魚和頭條等大項目的方式。個中利弊上述推薦均有提到。
開始
-
在已有項目根目錄運行命令
flutter create -t module my_flutter
不一定非在項目下創建,兼顧ios,位置路徑尋址對即可
-
在項目的
settings.gradle
文件添加如下代碼// MyApp/settings.gradle include ':app' // assumed existing content setBinding(new Binding([gradle: this])) // new evaluate(new File( // new settingsDir.parentFile, // new 'my_flutter/.android/include_flutter.groovy' // new )) // new
Binding飄紅不予理會,強迫癥的不用導包
'my_flutter/.android/include_flutter.groovy'為my_flutter文件路徑,報錯找不到可寫項目全路徑'ChannelFlutterAndroid/my_flutter/.android/include_flutter.groovy' -
在運行模塊app的build.gradle添加依賴
// MyApp/app/build.gradle dependencies { implementation project(':flutter') }
-
在原生Android端創建并添加flutterView
val flutterView = Flutter.createView(this, lifecycle, "route1") val layoutParams = FrameLayout.LayoutParams(-1, -1) addContentView(flutterView, layoutParams)
-
在Flutter端入口操作
void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'route1': return SomeWidget(...); default: return Center( child: Text('Unknown route: $route', textDirection: TextDirection.ltr), ); } }
至此,集成完畢
兩端通信Flutter Platform Channel的使用
本文主要是介紹使用方式,閑魚團隊詳盡的介紹了實現原理
Flutter提供 MethodChannel
、EventChannel
、BasicMessageChannel
三種方式
- 類似注冊監聽,發送的模式 原則
- 使用順序:先注冊,后發送,否則接收不到。尤其使用
MethodChannel
、EventChannel
不符合該原則會拋出異常,BasicMessageChannel
方式只是收不到消息
方式一 MethodChannel
應用場景:Flutter端 調用 Native端
Flutter端代碼:
var result = await MethodChannel("com.simple.channelflutterandroid/method",
StandardMethodCodec())
.invokeMethod("toast", {"msg": msg})
Android端代碼:
MethodChannel(flutterView, "com.simple.channelflutterandroid/method",
StandardMethodCodec.INSTANCE)
.setMethodCallHandler { methodCall, result ->
when (methodCall.method) {
"toast" -> {
//調用傳來的參數"msg"對應的值
val msg = methodCall.argument<String>("msg")
//調用本地Toast的方法
Toast.makeText(cxt, msg, Toast.LENGTH_SHORT).show()
//回調給客戶端
result.success("native android toast success")
}
"other" -> {
// ...
}
else -> {
// ...
}
}
}
注意點1:兩端需要對應一致的點
方法名稱:com.simple.channelflutterandroid/method,可以不采取包名,對應一致即可,不同的方式最好不要重復
傳參key:"msg"-
注意點2:以下為Flutter為例,Android同理
StandardMethodCodec()
非必傳,默認是StandardMethodCodec()
,是一種消息編解碼器對于
MethodChannel
和EventChannel
兩種方式,有兩種編解碼器,均實現自MethodCodec
,分別為StandardMethodCodec
和JsonMethodCodec
。對于
BasicMessageChannel
方式,有四種編解碼器,均實現自MessageCodec
,分別為StandardMessageCodec
、JSONMessageCodec
、StringCodec
和BinaryCodec
。 注意點3:setMethodCallHandler(),針對三種方式一一對應,屬于監聽
MethodChannel
-setMethodCallHandler()
EventChannel
-setStreamHandler()
BasicMessageChannel
-setMessageHandler()
其中setStreamHandler()方式稍微特殊,具體查看上面提的閑魚文章注意點4:
FlutterView
實現BinaryMessenger
接口
以上,以下兩種方法不再贅述
方式二 EventChannel
應用場景:Native端 調用 Flutter端,方式稍微特殊
Flutter端代碼:屬于接收方
EventChannel("com.simple.channelflutterandroid/event")
.receiveBroadcastStream()
.listen((event){
print("It is Flutter - receiveBroadcastStream $event");
})
注意 receiveBroadcastStream
Android端代碼:屬于發送方
EventChannel(flutterView, "com.simple.channelflutterandroid/event")
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(o: Any?, eventSink: EventChannel.EventSink) {
eventSink.success("我是發送Native的消息")
}
override fun onCancel(o: Any?) {
// ...
}
})
方式三 BasicMessageChannel
應用場景:互相調用
兩種使用方式,創建方式和格式不一樣
第一種
Flutter端
_basicMessageChannel = BasicMessageChannel("com.simple.channelflutterandroid/basic",
StringCodec())
// 發送消息
_basicMessageChannel.send("我是Flutter發送的消息");
// 接收消息
_basicMessageChannel.setMessageHandler((str){
print("It is Flutter - receive str");
});
Android端
val basicMessageChannel = BasicMessageChannel<String>(flutterView, "com.simple.channelflutterandroid/basic",
StringCodec.INSTANCE)
// 發送消息
basicMessageChannel.send("我是Native發送的消息")
// 接收消息
basicMessageChannel.setMessageHandler { o, reply ->
println("It is Native - setMessageHandler $o")
reply.reply("It is reply from native")
}
其中StringCodec(),四種方式可選
第二種
Flutter端
static const BASIC_BINARY_NAME = "com.simple.channelflutterandroid/basic/binary";
// 發送消息
val res = await BinaryMessages.send(BASIC_BINARY_NAME, ByteData(256))
// 接收消息
BinaryMessages.setMessageHandler(BASIC_BINARY_NAME, (byteData){
println("It is Flutter - setMessageHandler $byteData")
});
Android端
const val BASIC_BINARY_NAME = "com.simple.channelflutterandroid/basic/binary"
// 發送消息
flutterView.send(BASIC_BINARY_NAME,ByteBuffer.allocate(256));
// 接收消息
flutterView.setMessageHandler(BASIC_BINARY_NAME) { byteBuffer, binaryReply ->
println("It is Native - setMessageHandler $byteBuffer")
binaryReply.reply(ByteBuffer.allocate(256))
}
此組合可以進行圖片的傳遞
help 在操作中使用此方式傳輸數據總報錯,所以只傳了默認ByteBuffer。還是姿勢不對???目前未發現,求大神指教
如有錯誤之處,萬望各位指出!謝謝
其他
- Native端接收方收到消息后都能進行回傳信息,在Flutter進行異步接收
MethodChannel:result.success("我是回傳信息");
EventChannel:eventSink.success("我是回傳信息");
BasicMessageChannel:binaryReply.reply(-);- Flutter端:ByteData;Android端:ByteBuffer
Q&A
以上均為理想情況,過程中還是存在許多問題,以下提供參考
Q:使用命令行flutter create創建的project或者module,文件android/ios為隱藏打點開頭的.android/.ios文件
A:所以有的時候會出現問題,尋找不到文件的情況
Q: flutter create module 和project的區別
A: ?? 目前沒發現
Q:couldn't locate lint-gradle-api-26.1.2.jar for flutter project
A: https://stackoverflow.com/questions/52945041/couldnt-locate-lint-gradle-api-26-1-2-jar-for-flutter-project
Q: Could not resolve all files for configuration ':app:androidApis'.
Failed to transform file 'android.jar' to match attributes {artifactType=android-mockable-jar, returnDefaultValues=false} using transform MockableJarTransform
A:https://github.com/anggrayudi/android-hidden-api/issues/46
Q: Flutter Error: Navigator operation requested with a context that does not include a Navigator
A: https://github.com/flutter/flutter/issues/15919