flutter調用android原生插件功能的示例

做過很多的混合開發項目,有一些要調用系統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類似
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章