做过很多的混合开发项目,有一些要调用系统API的功能还是需要原生开发人员提供相关的SDK插件来实现,还有特定一些需求,比如开发个水印相机等,Android开发的插件SDK一般都是以.aar提供,那么flutter怎么和原生交互呢?下面我们就通过一个简单的Toast案例了解一下
核心API MethodChannel
就是负责flutter和Android交互
- 首先flutter端准备工作
1、定义通道static const platform = const MethodChannel('com.itplus.io/toast');
2、调用方法platform.invokeMethod("showToast");
,showToast
就是Android端用来过滤调用的方法
具体代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ToastPlugin extends StatefulWidget {
@override
_ToastPluginState createState() => _ToastPluginState();
}
class _ToastPluginState extends State<ToastPlugin> {
static const platform = const MethodChannel('com.itplus.io/toast');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("flutter调用Android原生功能"),
),
body: Center(
child: RaisedButton(
child: Text("弹个Toast"),
onPressed: () {
_showAndroidNativeToast();
},
),
));
}
_showAndroidNativeToast() async{
platform.invokeMethod("showToast");
}
}
- Android端具体实现
1、找到Android 的MainActivity
2、在onCreate
方法中创建MethodChannel
,并设置MethodCallHandler
3、定义和flutter中相同的通道值private val CHANNEL = "com.itplus.io/toast"
,必须保持一致
具体代码如下:
【kotlin版本】
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.itplus.io/toast"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "showToast") {
Toast.makeText(this,"flutter 调用 Android 原生",0).show();
result.success("ok")
} else {
result.notImplemented()
}
}
}
}
【java版本】
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.itplus.io/toast";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
if ( "showToast".equal(call.method) {
Toast.makeText(this,"flutter 调用 Android 原生",0).show();
result.success("ok")
} else {
result.notImplemented()
}
}
});
}
}
上面的例子很简单,就是弹出个Toast,要是传递过来参数弹出提示怎么搞呢?
Future<T> invokeMethod<T>(String method, [ dynamic arguments ])
,invokeMethod
有个可选参数arguments
,我们可以通过这个参数给Android端传递参数
_showAndroidNativeToast() async{
//platform.invokeMethod("showToast"); 修改如下
platform.invokeMethod("showToast","我是来自flutter的参数");
}
- Android端
MethodCall
有个参数public final Object arguments;
用来接收flutter端传过来的参数
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "showToast") {
// Toast.makeText(this,"flutter 调用 Android 原生",0).show();
Toast.makeText(this,call.arguments.toString(),0).show();
result.success("ok")
} else {
result.notImplemented()
}
}
如果flutter需要获取一个返回值怎么搞呢?
MethodChannel
类有个内部接口Result
,可以通过这个接口返回想要的值
比如,我们可以在flutter中获取一个结果返回结果 ,android 中用
result.Success(Object)
如下操作
_showAndroidNativeToast() async {
// platform.invokeMethod("showToast");
var result = await platform.invokeMethod("showToast", "我是来自flutter的参数");
print("获取android返回结果:");
print(result);
}
返回结果如图:
最后附上MethodCall源码:
public final class MethodCall {
public final String method;
public final Object arguments;
public MethodCall(String method, Object arguments) {
if (method == null) {
throw new AssertionError("Parameter method must not be null.");
} else {
this.method = method;
this.arguments = arguments;
}
}
public <T> T arguments() {
return this.arguments;
}
/***
* 可以看到json和Map都已经帮我处理了
*/
@Nullable
public <T> T argument(String key) {
if (this.arguments == null) {
return null;
} else if (this.arguments instanceof Map) {
return ((Map)this.arguments).get(key);
} else if (this.arguments instanceof JSONObject) {
return ((JSONObject)this.arguments).opt(key);
} else {
throw new ClassCastException();
}
}
public boolean hasArgument(String key) {
if (this.arguments == null) {
return false;
} else if (this.arguments instanceof Map) {
return ((Map)this.arguments).containsKey(key);
} else if (this.arguments instanceof JSONObject) {
return ((JSONObject)this.arguments).has(key);
} else {
throw new ClassCastException();
}
}
}
总结
我们要调用具体插件的方法都是有call.method 决定的,直接switch去不同的方法,跟Cordova中的action类似