做過很多的混合開發項目,有一些要調用系統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類似