Flutter 说到底只是一个 UI 框架,很多功能都需要通过原生的 Api 来实现,那么就会涉及到 Flutter 和 Native 的交互,因为本人不懂 iOS 开发,所以只能讲下 Flutter 同 Android 的交互。
Android 项目配置 Flutter 依赖
既然是互相交互,那么需要准备一个 Android 项目。接着就需要创建 flutter module,让 Android 项目依赖,创建的方法可以参考官网 Flutter Wiki,虽然是官网提供的方法,但是完全按照这个步骤来,还是会有坑的,这边就慢慢一步步解决坑。
如果你用的是 Android Studio 进行开发的话,直接打开底部的 Terminal,直接创建 flutter module 依赖
flutter create -t module flutter_native_contact
至于 module 名可以随意填写,module 创建完后结构大概是这样的
接着切换到 module 下的 .android 文件夹,接着有坑来了,官网提供的方法是 ./gradlew flutter:assembleDebug
可能会提示命令不存在,那么直接通过 gradlew flutter:assembleDebug
来运行,等它自动跑完后,打开根目录下的 settings.gradle
文件,加入官网提供的 gradle 代码
1 | setBinding(new Binding([gradle: this])) // new |
你以为这里没坑,真是图样图森破,没坑是不可能的,编译器大爷可能会给你甩这么个错误
很明显可以看出是找不到我们的文件,所以把文件名路径给补全
1 | evaluate(new File( // new |
接着打开原有项目下,原有项目下,原有项目下的 app 中的 build.gradle
文件,在 android 下加上如下代码
1 | compileOptions { |
这个必须要加,不要问为什么,我也不知道为什么,最后在项目下添加 flutter module 的依赖就完成了。这个过程告诉我们一个什么道理呢?*不要以为官网的都对,官网讲的也不是完全可信的,时不时给你来个坑就能卡你老半天。
原生界面加载 Flutter 页面
那么如何在原生界面显示 Flutter 界面呢,这个就需要通过 FlutterView 来实现了,Flutter 这个类提供了 createView
和 createFragment
两个方法,分别用于返回 FlutterView 和 FlutterFragment 实例,FlutterFragment 的实现原理也是通过 FlutterView 来实现的,可以简单看下 FlutterFragment 的源码
1 | /** |
createFragment 方式加载
在原生页面显示 Flutter 界面的第一种方式就是加载 FlutterFragment,看个比较简单的例子吧
1 | <?xml version="1.0" encoding="utf-8"?> |
在 Activity 可以直接通过返回 FlutterFragment 加载到 FrameLayout 即可
1 | class MainActivity : AppCompatActivity() { |
这样就把 Flutter 页面加载到原生界面了,会通过传递的路由值在 dart 层进行查找,所以接着就需要编写 Flutter 界面
1 | /// runApp 内部值也可以直接传入 _buildWidgetForNativeRoute 方法 |
运行后可以看到页面加载出来了,不过会有一段时间的空白,这个在正式打包后就不会出现,所以不必担心。最后的页面应该是这样的
createView 方式加载
接着看下 createView 方法,说白了,第一种方法最后还是会通过该方式实现
1 |
|
通过 createView 方法返回的 FlutterView,通过设置 Layoutparams 参数就可以添加到相应的布局上,还有一种直接通过 addContentView 方式进行加载,这里直接修改原有代码,
1 | override fun onCreate(savedInstanceState: Bundle?) { |
但是通过这样加载的话,那么整个页面都是 flutter 的页面。那么之前的效果的 FAB 则不会被加载出来了,即使没有省略 setContentView(R.layout.activity_main)
方法,这个页面的 xml 布局也会被覆盖。
PlantformChannel
那么能够在原生界面显示 flutter 页面了,如何互相交互呢,这就需要通过 PlantformChannel 来执行了,PlantformChannel 主要有三种类型,BasicMessageChannel,MethodChannel,EventChannel。通过查看源码可以发现,三个 Channel 的实现机制类似,都是通过 BinaryMessenger 进行信息交流,每个 Channel 通过传入的 channel name 进行区分,所以在注册 Channel 的时候必须要保证 channel name 是唯一的,同时需要传入一个 BinaryMessageHandler 实例,用于传递信息的处理,当 Handler 处理完信息后,会返回一个 result,然后通过 BinaryMessenger 将 result 返回到 Flutter 层。如果需要深入理解这边推荐一篇文章深入理解Flutter PlatformChannel
接下来直接看例子吧,在创建 PlatformChannel 的时候需要传入一个 BinaryMessenger 实例,通过查看 FlutterView 的源码可以发现,FlutterView 就是一个 BinaryMessenger 在 Android 端的实现,所以呢,可以直接通过前面介绍的 Flutter.createView
方法获取注册 Channel 时的 BinaryMessenger 实例了,真是得来全部费工夫~因为通信的方法可能在多个界面会使用,所以还是封装一个通用类来处理会比较合理
BasicMessageChannel
BasicMessageChannel 用于传递字符串和半结构化的信息。
1 | class FlutterPlugin(private val flutterView: FlutterView) :BasicMessageChannel.MessageHandler<Any>{ |
接着就需要有个 FlutterView 用来注册,新建一个 Activity,用于加载 Flutter 页面
1 | class ContactActivity : AppCompatActivity() { |
那么我们就要在 Flutter 界面的 _buildWidgetForNativeRoute
方法加入新路由值对应的界面
1 | Widget _buildWidgetForNativeRoute(String route) { |
最后的效果小伙伴可以自行执行,点击按钮后会弹出吐司,吐司内容就是 Flutter 传递的信息,同时在控制台可以看到从原生层返回的信息。
MethodChannel
MethodChannel 用于传递方法调用(method invocation)
直接在上述例子中进行修改,例如在 Flutter 页面中实现 Activity 的 finish 方法,并传递参数到前一个界面,先做 Flutter 页面的修改,在 AppBar 上增加一个返回按钮,用于返回上层页面
1 | class FlutterContactPage extends StatelessWidget { |
同时,我们需要在 FlutterPlugin 这个类中,做些必要的修改,首先需要实现 MethodCallHandler
接口,该接口中需要实现 onMethodCall
方法,通过获取调用的方法名和参数值,进行相应的处理
1 | class FlutterPlugin(private val flutterView: FlutterView) : |
最终的效果,当点击返回按钮的时候,会将 Flutter 层通过 invokeMethod 传递的 arguments 属性吐司出来,同时,控制台会打印出 “has finish” 的信息
EventChannel
EventChannel 用于数据流(event streams)的通信
EventChannel 的实现方式也类似,EventChannel 可以持续返回多个信息到 Flutter 层,在 Flutter 层的表现就是一个 stream,原生层通过 sink 不断的添加数据,Flutter 层接收到数据的变化就会作出新相应的处理。在 Android 端实现状态的监听可以通过广播来实现。直接看例子,还是修改上述代码
1 | class FlutterPlugin(private val flutterView: FlutterView) : |
在 Flutter 层,通过对 stream 的监听,对返回的数据进行处理,为了体现出变化,这边修改成 SatefulWidget 来存储状态
1 | class FlutterContactPage extends StatefulWidget { |
同时,需要在 Activity 层调用一个定时任务不断的发送广播
1 | class ContactActivity : AppCompatActivity() { |
最后的实现效果大概是这样的
Flutter 同 Android 端的交互到这讲的差不多了,和 iOS 的交互其实也类似,只不过在 Android 端通过 FlutterNativeView 来作为 Binarymessenger 的实现,在 iOS 端通过 FlutterBinaryMessenger 协议实现,原理是一致的。至于 Flutter 插件,其实现也是通过以上三种交互方式来实现的,可能我们目前通过 FlutterView 来作为 BinaryMessenger 实例,插件会通过 PluginRegistry.Registrar 实例的 messenger() 方法来获取 BinaryMessenger 实例。
最后贴上 demo 的地址:ContactDemo
需要了解插件的写法也可以直接查看官方提供的检测电量插件:Flutter Battery Plugin