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 的 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 直接實現。

那怎么實現呢?

欲知詳情,且聽下回講解

本文源碼位置:
https://github.com/nesger/FlutterSample/tree/feature/event_channel

參考鏈接:

https://flutter.dev/docs/development/platform-integration/platform-channels
https://github.com/flutter/flutter/tree/master/examples/platform_channel

更多閱讀:
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 妙用及 .. 使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM