flutter踩坑记录目录

flutter踩坑记录(一)--项目准备阶段   

flutter踩坑记录(三)-- 项目打包  

通过上一篇的踩坑记录(一),我相信你此时已经集成flutter模块到原生项目中,也迫不及待去使用flutter和体验flutter与原生的交互(目前大多数使用场景还是要与原生集成混合开发的)

Android中创建Flutter UI

Flutter提供两种方法引入,一个是View,一个是Fragment,(目前使用我使用的view方式引用,这种方式也更灵活方法,有一个viewGroup载体就可加载flutterView)

在oncreat中添加view(下面提到的rootLayout即为布局内一个空的ConstraintLayout的viewId,使用kotlin只需要有id就可使用view)

布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible"
        android:id="@+id/rootLayout"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

activity中代码文件

val flutterView = Flutter.createView(this, lifecycle, "home_page")
val layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT,
        ConstraintLayout.LayoutParams.MATCH_PARENT)
rootLayout.addView(flutterView, layoutParams)

createView方法说明:

第二个参数是Lifecycle对象,第三个参数是route(flutter页面跳转路径),这个参数Flutter端可以通过window.defaultRouteName获取

此时原生端加载flutterView载体已经搭建好,我们去看下flutter部分

dart代码的编写

import 'package:flutter/material.dart';
import 'list.dart';
 
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes: {
"home_page": (context) => MyHomePage(),
},
);
}
}

此时已经实现了原生页面中加入了flutterView的页面显示,可对于我们来说还远远不够,更重要的原生与flutter交互应该是大部分原生开发者更关心的方面

参考链接 

Andriod 原生与flutter交互

google为原生与flutter交互创建了channel渠道来实现,主要有3种,message,method,engine,目前常使用的是前2种

method channel 实现

原生activity内代码:

首先我们先定义channel 名称

private val CHANNEL = "demo.plugin"

method  channel 交互使用主要是使用了回调方式,这一点和原生与h5交互很类似,主要可用于flutter调用原生信息,调用方法等

//从flutter中调回原生页面
        MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
            when {
                call.method == "interaction" -> {
//                    val intent = Intent(this@FlutterTestPageActivity,
//                            PersonalSettingActivity::class.java)
//                    startActivity(intent)
                    val id = call.arguments as Int
                    ToastUtil.show(mContext!!,"收到的id为$id")
                }
                call.method == "methodId" -> result.success(12)
                else -> result.notImplemented()
            }
        }

flutter部分大部分代码实现

class _MyHomePageState extends State<MyHomePage> {
//原生与flutter部分渠道名一定要统一
final demoPlugin = const MethodChannel('demo.plugin');
num id = 0;
@override
void initState() {
super.initState();
getMethodId();
}
 
getMethodId() async {
var id = await demoPlugin.invokeMethod('methodId');
setState(() {
this.id = id;
});
}
widget显示部分
Text('需要的id为${this.id}'),
Container(
padding: EdgeInsets.only(top: 10.0),
child: RaisedButton(
child: Text('现在是Flutter'),
onPressed: () {
demoPlugin.invokeMethod('interaction', 123);
},
),
),

方法解释说明:

call.method == "interaction"  用来从flutter给原生发送信息或者打开原生页面的实现

call.method == "methodId"   用来flutter页面初始化从原生处取所需消息

message channel 渠道实现

message channel 交互使用主要是方便实现flutter与原生的消息传递,例如初始化页面时传递token给flutter页面,或者flutter按钮点击操作传递信息回原生页面,主要用于字符串等传递,主要使用 BasicMessageChannel传递

activity部分代码

private val CHANNEL = "demo.plugin"
private var messageChannel: BasicMessageChannel<String>? =null(string是自定义的传递消息的泛型)
val flutterView = Flutter.createView(this,lifecycle,"flutter_view")
val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
        LinearLayout.LayoutParams.MATCH_PARENT)
linearLayout.addView(flutterView,layoutParams)
/**
 * 初始化通信通道
 * 参数一:FlutterView实例对象
 * 参数二:消息通道名字(key)
 * 参数三:消息解码器(用于处理通信消息类型)
 */
messageChannel = BasicMessageChannel(flutterView,CHANNEL, StringCodec.INSTANCE)
//处理来自Flutter中发送的消息
messageChannel!!.setMessageHandler { p0, p1 ->
    //具体处理接收到的从Flutter中发送的消息 p0
    Log.e("setMessageHandler",p0)
    p1?.reply("")
}
//延时发送,确保页面初始化完成后发送数据
Handler().postDelayed({run {
    messageChannel?.send(accessToken!!)
}},2000)

此时activity对于message channel的设置已完成

dart部分代码

//注册消息通道
static const BasicMessageChannel<String> platform =
BasicMessageChannel<String>("demo.plugin", StringCodec());
String message = "测试";
widget部分代码
Column(
children: <Widget>[
Text(message),
RaisedButton(
onPressed: () {
platform.send('给原生页面发消息');
},
child: Text('给原生页面发消息'),
),
InkWell(
child: Text('获取ID'),
onTap: () {
_getPicUploadId();
},
)
],
)
具体处理messageHandler逻辑
Future<String> _handleMessage(String message) async {
setState(() {
this.message = message;
// SharePreferencesUtil().setString('token', message);
});
return "";
}
 
@override
void initState() {
super.initState();
platform.setMessageHandler(_handleMessage);
}

至此已展示两种常用消息通道的基本使用,如需更详细使用可参考Flutter Platform channel详解