之前我们分享过flutter的app开发,不过那是单纯的flutter开发,实际使用中因为flutter通用与Android与IOS所以一般采用的应该是Android与flutter的混合的开发。
今天作此笔记,记录 Android 与 flutter 混合开发的 的记录,以及测试Android与flutter之间的通信方式。
目录
一、Android与flutter混合开发
二、Android 与 flutter 之间的消息通信
2.1、MethodChannel 方式传递信息(单向)
2.2 EventChannel 方式传递信息(单项)
2.3 BasicMeaageChannel 方式传递消息(双向的)
首先我们在Android studio 中新建一个Android项目(此处使用的 kotlin)。
Android 项目新建完成之后,我们新建一个flutter项目,new->new flutter project,选中 flutter module
当flutter项目构建好了之后,我们在Android项目中以module导入flutter项目。new->new module,选中 import flutter module,点击next选则我们刚才新建的flutter module
然后等待项目构建完成。
完成之后我们看以下项目中的一些变化 ,首先是 app下面的build.gradle增加了依赖
settings.gradle增加了setBinding
在项目的目录中也有flutter的依赖,与Android的module相同。
module已经导入了,接下来我们需要从Android中集成flutter的页面了。为了便于观察我们新建一个 FlutterActivity 来加载flutter部分,MainActivity 作为原生的界面跳转入口。
在MainActivity中添加两个按钮用来跳转,xml布局文件修改如下:
在MainActivity中为两个按钮添加点击跳转事件:
btn_jump_f1.setOnClickListener {
startActivity("pager" to 1)
}
btn_jump_f2.setOnClickListener {
startActivity("pager" to 2)
}
FlutterActivity 的代码如下
class FlutterActivity :AppCompatActivity(){
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter)
//创建flutter view
val flutterView=Flutter.createView(this,lifecycle,"addflutter")
val params = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT , LinearLayout.LayoutParams.MATCH_PARENT )
linear_flutter.addView(flutterView,params)
}
注意:使用 startActivity
implementation 'org.jetbrains.anko:anko:0.10.4'
还需要修改 build.gradle 中的 minSdkVersion 修改为 26否则运行程序的时候会报错
Error: Invoke-customs are only supported starting with Android O (--min-api 26)
运行之后的界面:
上右图可以看到典型的flutter新项目的界面图:
我们实现了flutter的页面的加载。在创建flutterview的时候我们给出了3个参数 activity , lifecycle ,name 。其中 name 是作为一个标识参数在传递到flutter页面之后,我们可以根据这个值来区分我们加载的页面。flutter端获取此值的代码为:window.defaultRouteName
首先我们在跳转到FlutterActivity页面时 携带的参数 pager 是不同的,可以根据这个值来区分flutter加载不同的页面,修改flutter端的代码:
void main() => runApp(MyApp(route: window.defaultRouteName,));
class MyApp extends StatelessWidget {
// This widget is the root of your application.
String route;
MyApp({this.route});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "flutter_pager",
home: _getHomeByRoute(),
);
}
Widget _getHomeByRoute(){
switch(route){
case "pager1":
return PagerOne(title: route,);
default:
return PagerTwo(title: route,);
}
}
}
class PagerOne extends StatefulWidget {
PagerOne({Key key, this.title}) : super(key: key);
final String title;
@override
_PagerOneState createState() => _PagerOneState();
}
class _PagerOneState extends State {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("this is pager 1"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'this is pager 1',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PagerTwo extends StatefulWidget {
PagerTwo({Key key, this.title}) : super(key: key);
final String title;
@override
_PagerTwoState createState() => _PagerTwoState();
}
class _PagerTwoState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("pager 2"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'this is pager 2',
),
],
),
),
);
}
}
flutter端将根据 window.defaultRouteName 获取的字符串来决定加载 PagerOne 或者 PagerTwo.修改Android端FlutterActivity的代码为:
class FlutterActivity :AppCompatActivity(){
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter)
val index=intent.getIntExtra("pager",1);
tv_mark.text="${tv_mark.text} pager=$index"
val pager= if(index==1){
"pager1"
}else{
"pager2"
}
//创建flutter view
val flutterView=Flutter.createView(this,lifecycle,pager)
val params= LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
linear_flutter.addView(flutterView,params)
}
}
运行代码的示例:
页面没做美化看起来可能比较丑,不过我们现在只是为了实现根据参数加载不同页面的效果而已,功能已经实现了,接下来我们开始实现 Android与flutter的消息互发。
此方式是 把 Android 作为接受方,flutter 作为发送方。首先我们需要在Android 端注册 MethodChannel
MethodChannel(flutterView,"accept_from_flutter")
.setMethodCallHandler { methodCall, result ->
Log.d("+++++++++++",methodCall.method)
tv_accept_from_flutter.text=methodCall.argument("android")
}
在flutter端设置发送的方法:
class _PagerTwoState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("pager 2"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'this is pager 2',
),
RaisedButton(
child: Text("MethodChannel 给Android发送消息"),
onPressed: (){
_methodChannelSendMessageToAndroid();
},
),
],
),
),
);
}
//消息发送
void _methodChannelSendMessageToAndroid() async {
var result=await MethodChannel("accept_from_flutter").invokeMapMethod("option",{"android":"我是MethodChanner flutter给android发送的消息"});
}
}
运行示例
需要注意的地方是 Android端的 MethodChannel(flutterview,name)与flutter端MethodChannel(name),两个方法中的name必须相同,下面涉及到的其他消息发送方式也需要注意此项
此方式是把Android 作为信息发送方,首先需要在Android 端设置EventChannel 的异步发送方法,并且需要把EventChannel.StreamHandler中的onListen方法中的 eventSink 参数传递到外界,因为我们需要使用 eventSink.success来发送消息。
class FlutterSendActivity :AppCompatActivity(){
var eventSink:EventChannel.EventSink?=null
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter_send)
//创建flutter view
val flutterView=Flutter.createView(this,lifecycle,"pager3")
val params= LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
linear_flutter.addView(flutterView,params)
EventChannel(flutterView,"send_to_flutter")
.setStreamHandler(object:EventChannel.StreamHandler{
override fun onListen(p0: Any?, event: EventChannel.EventSink?) {
eventSink=event
}
override fun onCancel(p0: Any?) {
print("++++++++++++++++")
}
})
btn_send_to_flutter.setOnClickListener {
eventSink!!.success("这是Android发送给flutter的消息")
}
}
}
在flutter 端我们需要设置同样的EventChannel 用来接受消息。接收信息的方法为EventChannel("send_to_flutter").receiveBroadcastStream().listen()来接受消息,此用法的返回值是StreamSubscription
class _PagerThreeState extends State {
String content="this is pager 3";
StreamSubscription
运行示例:
需要注意的地方是 Android端的 EventChannel(flutterview,name)与flutter端EventChannel(name),两个方法中的name必须相同,下面涉及到的其他消息发送方式也需要注意此项
Android端同样需要注册接收方法
class FlutterBasicMessageChannelActivity :AppCompatActivity(){
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter_basic)
//创建flutter view
val flutterView=Flutter.createView(this,lifecycle,"pager4")
val params= LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
linear_flutter.addView(flutterView,params)
val basicChannel=BasicMessageChannel(flutterView,"basicChannel", StringCodec.INSTANCE)
//BasicMessageChannel 接受消息方法
basicChannel.setMessageHandler { string, reply ->
tv_info_from_flutter.text=string
}
btn_send_to_flutter.setOnClickListener {
//BasicMessageChannel 发送消息方法
basicChannel.send("我是BasicMessageChannel Android 发给 flutter的消息")
}
}
}
flutter端同样需要注册
class PagerFour extends StatefulWidget {
PagerFour({Key key, this.title}) : super(key: key);
final String title;
@override
_PagerFourState createState() => _PagerFourState();
}
class _PagerFourState extends State {
String content="this is pager 4";
BasicMessageChannel _basicMessageChannel=BasicMessageChannel("basicChannel", StringCodec());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("pager 4"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("BasicMessageChannel 接收来自 Android的消息"),
Text( content, ),
RaisedButton(
child: Text("BasicMessageChannel 给 Android发送消息"),
onPressed: (){
_sendMessageToAndroid();
},
)
],
),
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
//BasicMessageChannel的消息接受方法
_basicMessageChannel.setMessageHandler((str){
setState(() {
content=str;
});
});
}
//发送方法
void _sendMessageToAndroid(){
_basicMessageChannel.send("我是 BasicMessageChannel 给Android的发送消息");
}
}
运行示例:
需要注意的地方是 Android端的 BasicMessageChannel(flutterview,name)与flutter端BasicMessageChannel(name),两个方法中的name必须相同.
现在Android与flutter的通信实现了,我们可以使用各种参数的传递来实现我们所需要功能了。