Flutter 即學即用系列部落格——09 EventChannel 實現原生與 Flutter 通訊(一)

AndroidTraveler發表於2019-03-22

Flutter 即學即用系列部落格——09 EventChannel 實現原生與 Flutter 通訊(一)

前言

緊接著上一篇,這一篇我們講一下原生怎麼給 Flutter 發訊號,即原生-> Flutter

還是通過 Flutter 官網的 Example 來講解。

案例

接著上一次,這一次我們讓原生主動將電池的充電狀態傳送給 Flutter 並在介面顯示。

步驟如下。

1. Flutter 介面修改

我們在原先基礎上增加一列用於顯示文字。

String _chargingStatus = 'Battery status: unknown.';
Text(_chargingStatus),
複製程式碼
2. Flutter 定義 EventChannel

我們在 _BatteryWidgetState 裡面加入下面變數:

static const EventChannel eventChannel = EventChannel('samples.flutter.io/charging');
複製程式碼

samples.flutter.io/charging 可以自己指定,一般保證唯一,所以 samples 實際使用可以替換為包名。主要是要跟原生對應即可。

3. Flutter 在 initState 實現 EventChannel 監聽並實現對應回撥方法
  @override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

  void _onEvent(Object event) {
    setState(() {
      _chargingStatus =
      "Battery status: ${event == 'charging' ? '' : 'dis'}charging.";
    });
  }

  void _onError(Object error) {
    setState(() {
        PlatformException exception = error;
        _chargingStatus = exception?.message ?? 'Battery status: unknown.';
    });
  }
複製程式碼

可以看到如果原生髮送 charging 顯示 charging,否則顯示 discharging。

當然錯誤顯示的是原生髮送過來的錯誤資訊。

注意這裡如果要獲取到錯誤資訊,需要通過

PlatformException exception = error;
複製程式碼

這個轉換語句才可以。

4. 原生定義 EventChannel
private static final String CHARGING_CHANNEL = "samples.flutter.io/charging";
複製程式碼

注意需要跟 Flutter 的一一對應。

5. 原生建立 EventChannel 並通過 StreamHandler 的 EventSink 傳送內容給 Flutter
new EventChannel((FlutterView) flutterView, CHARGING_CHANNEL).setStreamHandler(
        new EventChannel.StreamHandler() {

            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {
            }

            @Override
            public void onCancel(Object arguments) {
            }
        }
);
複製程式碼

具體到這裡為:

new EventChannel((FlutterView)flutterView, CHARGING_CHANNEL).setStreamHandler(
        new EventChannel.StreamHandler() {
            private BroadcastReceiver chargingStateChangeReceiver;
            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {
                chargingStateChangeReceiver = createChargingStateChangeReceiver(events);
                registerReceiver(
                        chargingStateChangeReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
            }

            @Override
            public void onCancel(Object arguments) {
                unregisterReceiver(chargingStateChangeReceiver);
                chargingStateChangeReceiver = null;
            }
        }
);

private BroadcastReceiver createChargingStateChangeReceiver(final EventChannel.EventSink events) {
    return new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);

            if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
                events.error("UNAVAILABLE", "Charging status unavailable", null);
            } else {
                boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                        status == BatteryManager.BATTERY_STATUS_FULL;
                events.success(isCharging ? "charging" : "discharging");
            }
        }
    };
}
複製程式碼

這裡的 events.successevents.error 分別會呼叫 Flutter 的對應方法。

其中 error 的引數對應 Flutter 的 PlatformException 的引數。

PlatformException({
  @required this.code,
  this.message,
  this.details,
}) : assert(code != null);
複製程式碼

這裡通過廣播的方式將電量狀態變化傳送給 Flutter。

效果如下:

Flutter 即學即用系列部落格——09 EventChannel 實現原生與 Flutter 通訊(一)

擴充套件

其實我們點選 Flutter 的 EventChannel,會看到原始碼裡面的 receiveBroadcastStream 方法是對 MethodChannel 做了封裝。

Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);
    StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async {
      BinaryMessages.setMessageHandler(name, (ByteData reply) async {
        if (reply == null) {
          controller.close();
        } else {
          try {
            controller.add(codec.decodeEnvelope(reply));
          } on PlatformException catch (e) {
            controller.addError(e);
          }
        }
        return null;
      });
      try {
        await methodChannel.invokeMethod('listen', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'while activating platform stream on channel $name',
        ));
      }
    }, onCancel: () async {
      BinaryMessages.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod('cancel', arguments);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'while de-activating platform stream on channel $name',
        ));
      }
    });
    return controller.stream;
  }
複製程式碼

所以其實原生-> Flutter 的通訊也是可以用 MethodChannel 直接實現。

那怎麼實現呢?

欲知詳情,且聽下回講解

Flutter 即學即用系列部落格——09 EventChannel 實現原生與 Flutter 通訊(一)

本文原始碼位置:
github.com/nesger/Flut…

參考連結:

flutter.dev/docs/develo…
github.com/flutter/flu…

更多閱讀:
Flutter 即學即用系列部落格
Flutter 即學即用系列部落格——01 環境搭建
Flutter 即學即用系列部落格——02 一個純 Flutter Demo 說明
Flutter 即學即用系列部落格——03 在舊有專案引入 Flutter
Flutter 即學即用系列部落格——04 Flutter UI 初窺
Flutter 即學即用系列部落格——05 StatelessWidget vs StatefulWidget
Flutter 即學即用系列部落格——06 超實用 Widget 集錦
Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考
Flutter 即學即用系列部落格——08 MethodChannel 實現 Flutter 與原生通訊

Flutter & dart
dart 如何優雅的避空
Flutter map 妙用及 .. 使用

Flutter 即學即用系列部落格——09 EventChannel 實現原生與 Flutter 通訊(一)

相關文章