Flutter入門進階之旅(十九)Flutter與原平生臺交互

引言:

通過前面章節的學習,相信讀者已經對flutter有了一個總體的認識,而且也能利用flutter平臺提供的一些基礎組件本身寫一些簡單的頁面邏輯,甚至有些讀者可能已經在用純flutter開發屬於本身的app了,可是可能好多讀者都會感受到有些場景下或者說有些原平生臺的東西從flutter端是沒法獲取的,好比系統版本、電池電量、動態權限申請等系統級的API,flutter並無直接給我提供相關的API去操做,這個時候咱們可能就須要經過藉助Native的能力或者與原平生臺交互來獲取這些數據。java

課程目標

  • 瞭解並掌握flutter與原生通訊的方法
  • 掌握flutter與原生經過MethodChannel相互回調的實現機制
  • 掌握原平生臺經過EventChannel主動向Flutter傳遞數據
1.Flutter與原生通訊

flutter在與native端進行通訊主要藉助MethodChannel跟EventChannel創建鏈接,MethodChannel跟EventChannel就像管道或者橋樑同樣,把flutter端跟native鏈接到一塊。 咱們來看下官方對此的解釋:android

A named channel for communicating with platform plugins using asynchronous method calls. 使用異步方法調用與平臺插件通訊的指定通道。git

  • 1.)MethodChannel方法簽名
public MethodChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }
複製代碼
  • 2.)EventChannel方法簽名
public EventChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }
複製代碼

爲了保證用戶界面在交互過程當中的流暢性,不管是從Flutter向Native端發送消息,仍是Native向Flutter發送消息都是以異步的形式進行傳遞的。github

1.在整個交互過程當中,不管是Flutter端仍是Native端均可以經過MethodChannel向對方平臺發送兩端提早定義好的方法名來調用對方平臺相對應的消息處理邏輯而且帶回返回值給被調用方。 2.而EventChannel的使用場景更側重於Native平臺主動向Flutter平臺單向給Flutter平臺發送消息,Flutter沒法返回任何數據給Native端,筆者更願意把EventChannel描述成是單通的。app

到目前爲止,咱們已經簡單的對flutter於native端的通訊交互方式有了一個簡單的瞭解,下面咱們先來看貼上本節課程要完成的代碼效果圖,而後再對效果圖中提到的案例逐個分析講解:異步

平臺交互

2.效果圖代碼案例分析
  • Flutter端經過MethodChannel調用Native平臺彈出Toast
  • Flutter利用MechtondChannl向Natvie端傳遞參數調用Native端相關函數,Native接收參數後,並把處理完成後的結果返回給Flutter端。
  • Flutter端利用MethodChannel打開原生新頁面
  • Native端利用MethodChannel傳遞參數到Flutter端,並接收從Flutter端傳遞回來的處理後到數據
  • Native端利用EventChannel主動向Flutter端發送數據(消息)

兩端交互的方法註冊邏輯也比較簡單,讀者一看便知,我就不過多展開描述,下面貼上關於兩端交互的兩個表明性的例子:async

1.Flutter 調用原生函數,計算兩個數的和並獲取處理完成的結果到Flutter端 2.Native端利用EventChannel主動向Flutter端發送數據(消息)ide

2.1 Flutter 調用原生函數,計算兩個數的和並獲取處理完成的結果到Flutter端

場景分析 :可類比Flutter端處理不了的業務邏輯,這個時候把必要的參數傳遞給原生,原生處理接收到參數,處理完成後,再把結果回傳到Flutter端,完成整個業務邏輯。函數

註冊通道 爲了確保Flutter與Native能創建正常的通訊,咱們首先要保證兩端註冊的MethocChannel的通道名channelName一致,以下分別在兩端註冊channelName:學習

Native端:

private static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
 methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
複製代碼

Flutter端:

static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
   static final MethodChannel _MethodChannel =MethodChannel(METHOD_CHANNEL); //平臺交互通道
複製代碼

Android端:

public class MainActivity extends FlutterActivity {
    private static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
    private static final String METHOD_NUMBER_ADD = "numberAdd"; //簡單加法計算,並返回兩個數的和
    private MethodChannel methodChannel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
        GeneratedPluginRegistrant.registerWith(this);


        methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
        //接受fltuter端傳遞過來的方法,並作出響應邏輯處理
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                System.out.println(call.method);
               if (call.method.equals(METHOD_NUMBER_ADD)) {
                    int number1 = call.argument("number1");
                    int number2 = call.argument("number2");
                    result.success(number1 + number2); //返回兩個數相加後的值
                } 
            }
        });

    }
    
}
複製代碼

Flutter 端:

class AndroidPlatformPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PageState();
}

class PageState extends State<AndroidPlatformPage> {
  static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
  static final String NATIVE_METHOD_ADD = "numberAdd"; //原生android平臺定義的供flutter端喚起的方法名
  static final MethodChannel _MethodChannel = MethodChannel(METHOD_CHANNEL); //平臺交互通道

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("平臺交互"),
        centerTitle: true,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          RaisedButton(
            color: Colors.orangeAccent,
            child: Text("計算兩個數的和"),
            onPressed: () {
              getNumberResult(25, 36);
            },
          ),
        ],
      ),
    );
  }

 
  /** * 調用平臺方法計算兩個數的和,並調用原生toast打印出結果 */
  void getNumberResult(int i, int j) async {
    Map<String, dynamic> map = {"number1": 12, "number2": 43};
    int result = await _MethodChannel.invokeMethod(NATIVE_METHOD_ADD, map);
    print("調用原平生臺計算後的結果爲:${result}");
  }
}
複製代碼

經過MethodChannel傳遞參數而且獲取返回值,兩端的執行邏輯徹底同樣,不管是從Flutter端回調Native端端數據,仍是Native回調Flutter端數據都是先由被喚起端經過 methodChannel.setMethodCallHandlermethodChannel.invokeMethod(String method, [ dynamic arguments ]),一個負責處理回調,一個負責發送事件而且攜帶參數。

2.1.1事件接收處理端

接收處理回調時onMethodCall(MethodCall call, MethodChannel.Result result)經過methodCall接收事件發送者傳遞回來的信息,經過Result把處理完的結果發送給事件發送方。

  • 經過methodCall.method:來區分不一樣函數名(方法)名以執行不一樣的業務邏輯,
  • 經過methodCall.hasArgument("key"):判斷是否有某個key對應的value
  • 經過methodCall.argument("key"):獲取key對應的value值
  • 經過result.success(object):把處理完的結果返回給事件發送方
2.1.2事件發送端

處理事件發送方經過methodChannel.invokeMethod("方法名","要傳遞的參數")把須要傳遞的參數傳遞給事件監聽者。 其中

  • 方法名:不能爲空
  • 要傳遞的參數:能夠爲空,若不爲空則必須爲可Json序列化的對象。

而後監聽者經過在setMethodCallHandler作出相關操做,對傳遞過來的參數作出相對於的邏輯處理後再把結果傳遞回來,以此完成整個平臺交互過程。

上述例子flutter端做爲事件發送方,Native端做爲事件接收處理方,其實MethodChannel的設計徹底可讓兩者身份互換,即從Native端去發送消息到Flutter而後獲取返回結果,實現的邏輯就是上述的逆過程,在真實開發中應用中筆者認爲從原生端經過EventChannel主動向Flutter發消息的使用場景要遠遠多於Native端經過MethodChannel回調去處理Flutter端的返回數據,因此就不單獨在這裏展開贅述Native端經過MethodChannel回調去處理Flutter端返回的數據了,感興趣的讀者能夠查看本篇博客配套代碼中去查閱相關代碼示例跟註釋解讀,下面咱們來看下經過EventChannel主動向Flutter發消息的具體操做流程。

2.2 Native端利用EventChannel主動向Flutter端發送數據(消息)

場景分析:實現從Native端主動向Flutter端傳遞數據:頁面成功渲染後從原生向Flutter端發送一個字符串顯示在Flutter端端Text Widget上,當點擊Flutter界面上的按鈕後,調用原生的方法,主動向Flutter發送消息,更新Flutter端端UI顯示。

效果圖

Native主動向Flutter發送消息

android端代碼

public class MainActivity extends FlutterActivity {
    private static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
    private static final String EVENT_CHANNEL = "com.zhuandian.flutter/android/event"; //事件通道,供原生主動調用flutter端使用
    private static final String METHOD_NATIVE_SEND_MESSAGE_FLUTTER = "nativeSendMessage2Flutter"; //原生主動向flutter發送消息
    private EventChannel.EventSink eventSink;
    private MethodChannel methodChannel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
        //接受fltuter端傳遞過來的方法,並作出響應邏輯處理
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                System.out.println(call.method);
                 if (call.method.equals(METHOD_NATIVE_SEND_MESSAGE_FLUTTER)) {
                    nativeSendMessage2Flutter();
                }
            }
        });


        new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object o, EventChannel.EventSink eventSink) {
                MainActivity.this.eventSink = eventSink;
                eventSink.success("事件通道準備就緒");
                //在此不建議作耗時操做,由於當onListen回調被觸發後,在此註冊當方法須要執行完畢纔算結束回調函數
                //的執行,耗時操做可能會致使界面卡死,這裏讀者需注意!!
            }

            @Override
            public void onCancel(Object o) {

            }
        });


    }


    /** * 原生端向flutter主動發送消息; */
    private void nativeSendMessage2Flutter() {
        //主動向flutter發送一次更新後的數據
        eventSink.success("原生端向flutter主動發送消息");
    }
}

複製代碼

Flutter端代碼

class AndroidPlatformPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PageState();
}

class PageState extends State<AndroidPlatformPage> {
  static final String METHOD_CHANNEL = "com.zhuandian.flutter/android";
  static final String EVENT_CHANNEL = "com.zhuandian.flutter/android/event";

  static final String NATIVE_SEND_MESSAGE_TO_FLUTTER =
      "nativeSendMessage2Flutter"; //原生主動向flutter發送消息

  static final MethodChannel _MethodChannel =
      MethodChannel(METHOD_CHANNEL); //平臺交互通道
  static final EventChannel _EventChannel =
      EventChannel(EVENT_CHANNEL); //原平生臺主動調用flutter端事件通道

  String _fromNativeInfo = "";

  @override
  void initState() {
    super.initState();
    _EventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onErroe);
  }
  

  /** * 監聽原生傳遞回來的值(經過eventChannel) */
  void _onEvent(Object object) {
    print(object.toString() + "-------------從原生主動傳遞過來的值");
    setState(() {
      _fromNativeInfo = object.toString();
    });
  }

  void _onErroe(Object object) {
    print(object.toString() + "-------------從原生主動傳遞過來的值");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("平臺交互"),
        centerTitle: true,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Text("從原平生臺主動傳遞回來的值"),
          Text(_fromNativeInfo),
          RaisedButton(
            color: Colors.orangeAccent,
            child: Text("點擊調用原生主動向flutter發消息方法"),
            onPressed: () {
              _MethodChannel.invokeMethod(NATIVE_SEND_MESSAGE_TO_FLUTTER);
            },
          ),
        ],
      ),
    );
  }
}
複製代碼

經過對比MethodChannle回調的形式傳遞數據,結合EventChannel從Native端主動向Flutter端發送數據咱們能夠發現後者更注重的監聽,咱們在Native端經過註冊EventChannel對象後,而後經過EventChannel.EventSinkvoid success(Object var1)方法向Flutter發送數據。

而Flutter端,咱們經過在頁面初始化端時候綁定監聽對象

@override
  void initState() {
    super.initState();
    _EventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onErroe);
  }
複製代碼

而且經過_onEvent監遵從原生主動發送過來值,而後註冊相應的處理。

/** * 監聽原生傳遞回來的值(經過eventChannel) */
 void _onEvent(Object object) {
   print(object.toString() + "-------------從原生主動傳遞過來的值");
   setState(() {
     _fromNativeInfo = object.toString();
   });
 }
複製代碼

限於篇幅問題,我在博文開頭效果圖裏展現端代碼就不逐一講解效果圖上的代碼示例了完整的代碼在本篇文章對應的源代碼裏都沒有註釋,讀者可自行閱讀配套源碼結合代碼裏的註釋自行測試。 平臺交互部分完整代碼: Native端:github.com/xiedong11/f… Flutter端:github.com/xiedong11/f…

相關文章
相關標籤/搜索