1. EventChannel 简介
EventChannel 用于事件流的发送(event streams), 属于持续性的单向通信, 只能是 iOS 端主动调用, 常用于传递原生设备的信息, 状态等, 比如电池电量, 远程通知, 网络状态变化, 手机方向, 重力感应, 定位位置变化等等.
本文的代码是在上一篇文章iOS 与 Flutter 通信之 BasicMessageChannel的基础上做了相应的修改, 去掉多余的代码, 加上最新的 EventChannel 相关的代码, 具体完整代码在最后已经给出.
2. 使用场景
2.1 iOS 发送 event
2.1.1 场景描述
Flutter 模块初始化之后, 点击按钮弹出 flutter 页面, 同时开启一个定时器, 每1秒, 给 flutter 端发送一个字符串. flutter 收到数据之后, 在控制台输出收到的字符串, 点击 flutter 页面按钮 dismiss 的同时, 也停止定时器.
2.1.1 场景效果
3. 使用方法
定义 EventChannel 是肯定的, 但是与前两种不同的是, EventChannel 需要一个遵守 FlutterStreamHandler 协议的代理, 即设置为当前 ViewController, 这个代理有两个方法. 期中一个监听方法提供了一个FlutterEventSink 类型的 block 参数, 用于 event 发送 因此需要定义一个 FlutterEventSink 类型的 block 记录代理提供的 block, 以便随时使用其发送 event.
3.1 iOS 端代码
- 属性定义, 并遵守
FlutterStreamHandler协议, 此处只给出EventChannel相关属性.
@interface ViewController ()<FlutterStreamHandler>
@property (nonatomic, strong) FlutterEventChannel *eventChannel;
// typedef void (^FlutterEventSink)(id _Nullable event);
@property (nonatomic, strong) FlutterEventSink eventSink;
@end
- 初始化 channel 并设置代理,
// event channel 初始化
self.eventChannel = [FlutterEventChannel eventChannelWithName:@"eventChannel" binaryMessenger:self.flutterVC.binaryMessenger];
// 设置代理
[self.eventChannel setStreamHandler:self];
- 实现代理方法 并记录
eventSink
// FlutterStreamHandler
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events {
// 记录 event
self.eventSink = events;
return nil;
}
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
self.eventSink = nil;
return nil;
}
- 用定时器发送 event
// event channel -> 定时调用
static int a = 0; // 自增数字
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (weakSelf.eventSink) {
weakSelf.eventSink([NSString stringWithFormat:@"哈哈: %d", a++]);
}
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSRunLoopCommonModes];
[self.timer fire];
3.2 flutter 端代码
除了初始化 EventChannel 之外, 还要记录一下接收到的 event, 另外在开启监听时还会返回一个 StreamSubscription 的对象, 这个对象用于对 channel 的管理, 暂停, 恢复, 取消, 是否暂停状态等, 本文不对 StreamSubscription 做详细说明, 后续会单独进行说明.
- 初始化
EventChannel并定义记录相关属性
final EventChannel _eventChannel = const EventChannel('eventChannel');
late StreamSubscription _streamSubscription;
String eventStr = '';
- 监听 event 事件, 并记录管理对象.
event: 就是iOS端调用eventSink发送过来的事件流.
_streamSubscription = _eventChannel.receiveBroadcastStream().listen((event) {
print('received event: $event');
setState(() {
eventStr = event;
});
}, onError: (dynamic error) {
print('received error: ${error.message}');
}, cancelOnError: true);
4. 总结
EventChannel 通信特点:
- 用于 iOS 端向 Flutter 端传递事件.
- 单向通信, 只能是 iOS 发送事件, Flutter 监听.
- 可持续性通信.
5. 完整代码
5.1 iOS 原生代码
#import "ViewController.h"
#import <Flutter/Flutter.h>
@interface ViewController ()<FlutterStreamHandler>
@property (nonatomic, strong) FlutterEngine *flutterEngine;
@property (nonatomic, strong) FlutterViewController *flutterVC;
@property (nonatomic, strong) FlutterEventChannel *eventChannel;
// typedef void (^FlutterEventSink)(id _Nullable event);
@property (nonatomic, strong) FlutterEventSink eventSink;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
FlutterViewController *vc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
// 全屏时 flutter 页面就需要写回调来告诉原生 dismiss
vc.modalPresentationStyle = UIModalPresentationFullScreen;
self.flutterVC = vc;
// event channel 初始化
self.eventChannel = [FlutterEventChannel eventChannelWithName:@"eventChannel" binaryMessenger:self.flutterVC.binaryMessenger];
// 设置代理
[self.eventChannel setStreamHandler:self];
}
// FlutterStreamHandler
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events {
self.eventSink = events;
return nil;
}
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
self.eventSink = nil;
return nil;
}
- (FlutterEngine *)flutterEngine {
if (!_flutterEngine) {
FlutterEngine *flutterEngine = [[FlutterEngine alloc] initWithName:@"andy"];
if ([flutterEngine run]) {
_flutterEngine = flutterEngine;
}
}
return _flutterEngine;
}
// one page
- (IBAction)pushFlutter:(id)sender {
// 初始化 FlutterMethodChannel
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"channel_1" binaryMessenger:self.flutterVC.binaryMessenger];
[methodChannel invokeMethod:@"one" arguments: nil];
// event channel -> 定时调用
static int a = 0;
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (weakSelf.eventSink) {
weakSelf.eventSink([NSString stringWithFormat:@"哈哈: %d", a++]);
}
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSRunLoopCommonModes];
[self.timer fire];
[self presentViewController:self.flutterVC animated:YES completion:nil];
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if ([call.method isEqual:@"dismiss"]) {
[self dismissViewControllerAnimated:YES completion:nil];
[self.timer invalidate];
self.timer = nil;
}
}];
}
@end
5.2 Flutter 代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String pageIndex = '';
final MethodChannel _channel_1 = const MethodChannel('channel_1');
final EventChannel _eventChannel = const EventChannel('eventChannel');
late StreamSubscription _streamSubscription;
String eventStr = '';
@override
void initState() {
// TODO: implement initState
super.initState();
// event channel 监听
_streamSubscription = _eventChannel.receiveBroadcastStream().listen((event) {
print('received event: $event');
setState(() {
eventStr = event;
});
}, onError: (dynamic error) {
print('received error: ${error.message}');
}, cancelOnError: true);
_channel_1.setMethodCallHandler((call) async {
setState(() {
if (call.method == 'one') {
pageIndex = call.method;
}
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Module',
theme: ThemeData(
primarySwatch: Colors.blue
),
home: _rootPage(pageIndex),
);
}
Widget _rootPage(String pageIndex) {
switch(pageIndex) {
case 'one':
return Scaffold(
appBar: AppBar(
title: Text(pageIndex),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text(pageIndex),
onPressed: () {
_channel_1.invokeMethod('dismiss');
_streamSubscription.cancel();
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(eventStr)
],
)
],
)
);
}
return Scaffold(
appBar: AppBar(
title: const Text('hello flutter'),
),
body: const Center(
child: Text('hello flutter'),
),
);
}
}