1.嵌入Flutter

先创建ios工程

我们先创建NativeDemo ios工程然后,导入cocopad通过配置pod文件来引入Flutter 框架。

flutter ios实现 flutter写ios_ios

flutter_app_path = '../flutter_module'

load File.join(flutter_app_path,'.iOS','Flutter','podhelper.rb')

platform :ios, '11.0'

post_install do |installer|

  flutter_post_install(installer)

end


target 'NativeDemo' do

  install_all_flutter_pods(flutter_app_path)

  use_frameworks!

end

创建flutter_module工程,用于原生引入调用:

flutter ios实现 flutter写ios_二进制数_02

在来看ios原生如何嵌入Flutter页面:

#import <Flutter/Flutter.h>

@property(nonatomic, strong) FlutterEngine * flutterEngine;

@property(nonatomic, strong) FlutterViewController* flutterVc;

@property(nonatomic, strong) FlutterBasicMessageChannel* msgChannel;

- (IBAction)pushFlutter:(id)sender {
   //告诉Flutter显示one_page
    FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"one_page" binaryMessenger:self.flutterVc];
    [methodChannel invokeMethod:@"one" arguments:nil];
    //弹出Flutter页面
    [self presentViewController:self.flutterVc animated:YES completion:nil];
    //监听Flutter页面的回调
    [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        //如果是要退出
        if([call.method isEqualToString:@"exit"]){
            [self.flutterVc dismissViewControllerAnimated:YES completion:nil];
        }
    }];
}

- (IBAction)pushFlutterTwo:(id)sender {
    //告诉Flutter显示one_page
     FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"two_page" binaryMessenger:self.flutterVc];
    [methodChannel invokeMethod:@"two" arguments:nil];
     //弹出Flutter页面
     [self presentViewController:self.flutterVc animated:YES completion:nil];
     //监听Flutter页面的回调
     [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
         //如果是要退出
         if([call.method isEqualToString:@"exit"]){
             [self.flutterVc dismissViewControllerAnimated:YES completion:nil];
         }
     }];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.flutterVc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];

    self.msgChannel = [FlutterBasicMessageChannel  messageChannelWithName:@"messageChannel" binaryMessenger:self.flutterVc];
    [self.msgChannel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
        NSLog(@"收到Flutter的%@",message);
    }];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    static int a = 0;
    [self.msgChannel sendMessage:[NSString stringWithFormat:@"%d",a++]];
}

然后我们在看flutter_module工程代码:

void initState() {
    _messageChannel.setMessageHandler((message) {
      print('收到来自iOS的$message');
      return Future(() {});
    });

    super.initState();
    _oneChannel.setMethodCallHandler((call) {
      pageIndex = call.method;
      print(call.method);
      setState(() {});
      return Future(() {});
    });

    _twoChannel.setMethodCallHandler((call) {
      pageIndex = call.method;
      print(call.method);
      setState(() {});
      return Future(() {});
    });
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: _rootPage(pageIndex),
    );
  }

  _rootPage(String pageIndex) {
    switch (pageIndex) {
      case 'one':
        return Scaffold(
          appBar: AppBar(
            title: Text(pageIndex),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  _oneChannel.invokeMapMethod('exit');
                },
                child: Text(pageIndex),
              ),
              TextField(
                onChanged: (String str) {
                  _messageChannel.send(str);
                },
              )
            ],
          ),
        );
      case 'two':
        return Scaffold(
          appBar: AppBar(
            title: Text(pageIndex),
          ),
          body: Center(
            child: ElevatedButton(
              onPressed: () {
                _twoChannel.invokeMapMethod('exit');
              },
              child: Text(pageIndex),
            ),
          ),
        );
      default:
        return Scaffold(
          appBar: AppBar(
            title: const Text('default'),
          ),
          body: Center(
            child: ElevatedButton(
              onPressed: () {
                _twoChannel.invokeMapMethod('exit');
              },
              child: Text(pageIndex),
            ),
          ),
        );
    }
  }
}

运行效果:

下面是理论介绍:

2.Flutter Channel

Flutter 作为一个灵活的UI框架。无论是ios平台上的Objective-C或Swift,还是Android平台上的Java或Kotlin都可以通过Platform Channel 机制来与Flutter进行通讯。需要注意的是Platform Channel 不依赖代码生成,而是建立在消息传递方式上。实际上,它的工作模式和原理非常类似基于二进制协议开发的网络服务。

iOS Channel 原理

Flutter 提供了三种Channel用作Flutter与iOS原生平台之间的数据传递:

1.FlutterBasicMessageChannel:用作字符串和半结构化的数据传递。

**结构化数据:**: 包括预定义的数据类型、格式和结构的数据,常见的比如关系型数据库中数据表里的数据。

**半结构化数据:**: 具有可识别的模式并可以解析的文本数据文件,比如XML数据文件。

**非结构化数据:**: 没有固定结构的数据,通常保存为不同类型的文件,比如文本文档、图片、视频等。

2.FlutterMethodChannel:用来调用方法(method invocation),包括从 Flutter向原生平台发起方法调用,也支持从原生平台Flutter发起方法调用。

3.FlutterEventChannel:用来支持数据流(streams)通信。

三种 Channel 分别带来不同的作用。但是在设计上大同小异。都有以下三个成员变量:

  1. name:Channel 名称 作为每一个Channel的唯一标志。
    在我们的Flutter 应用中,通常会存在多个 Platform Channel。 那么这些 Channel 之间就是通过唯一标志name 来区分。 例如,使用 FlutterMethodChannel发起方法调用时,就需要我们为MethodChannel指定对应name。
  2. messenger:消息信使(BinaryMessenger)

用作消息的发送和接收的工具,主要负责Flutter与原生之间的相互通讯

通俗来讲, messenger就是咱们现在的外卖小哥。messenger负责把数据从 Flutter 送到iOS平台,或者从iOS传输数据到Flutter。尽管Flutter中存在三种不同用途的Channel, 但是对应的沟通工具都是 BinaryMessenger

在创建一个 Channel 后,不论是通过设置代理 (Delegate),还是通过 setXXXHandler:来进行消息处理。最终会为该 Channel 绑定一个 FlutterBinaryMessageHandler。 并以 Channel的name作为key , 保存在一个 Map 结构中。 当接受到发送消息后,会根据消息中携带的 Channel 名称取出对应 FlutterBinaryMessageHandler, 并交由BinaryMessenger处理。

注意:在 iOS平台BinaryMessenger 是一个名为 FlutterBinaryMessenger 的协议。

3 .Codec (编解码器)

在 Channel 中,messenger 携带的数据需要在 Dart 层, Native (iOS/Android平台)层中传输,所以就需要一种与平台无关的数据协议。既能支持图片,又能支持文件等资源。因此官方最终采用了二进制字节流作为数据传输协议。

二进制字节流: 发送方法需要把数据编码成二进制数据,接收方再把数据解码成原始数据。而负责编解码操作的就是 Codec。

在 Flutter 中有两种 Codec:

MessageCodec:对message进行编解码 MessageCodec 用于二进制数据与基础数据之间的编解码,其中 FlutterBasicMessageChannel 中采用的就是该Codec。

//用于实现二进制数据NSData和不同类型数据之间的转换
@protocol FlutterMessageCodec

+(instancetype)sharedInstance;

//将指定的类型message编码为二进制数据
- (NSData *_nullable)encode:(id _nullable)message;

//将二进制数据NSData解码成指定类型
-(id _nullable)decode:(NSData* _nullabel)message;

@end

在 Flutter 中,MessageCodec有多种实现:

  • FlutterStandardMessageCodec:是FlutterBasicMessageChannel 中默认使用的编解码器。(底层是用FlutterStandardReaderWriter 实现的)。用于数据类型和二进制数据之间的编解码。支持基础数据类型包(bool、char、double、float、int、long、short、String、Array、Dictionary)以及二进制数据。
  • FlutterBinaryCodec:用于二进制数据和二进制数据之间的编解码,在实现上只是原封不动的将接收到的二进制数据返回。
  • FlutterStringCodec:用于字符串与二进制数据之间的编解码,对于字符串采用 UTF-8编码格式。
  • FlutterJSONMessageCodec:用于数据类型与二进制数据之间的编解码,支持基础数据类型(bool、char、double、float、int、long、short、String、Array、Dictionary)。在iOS端使用 NSJSONSerialization作为序列化工具。

FlutterMethodCodec:对 FlutterMethodCall编解码

FlutterMethodCodec 用于二进制数据与方法调用(FlutterMethodCall)和返回结果之间的编解码。主要用在FlutterMethodChannel 和 FlutterEventChannel中。

@protocol FlutterMethodCodec

+(instancetype)sharedInstance;

//将FlutterMethodCall编码为二进制NSData
-(NSData *)encodeMethodCall:(FlutterMethodCall*)methodCall;

//将二进制NSData methodCall解码为FlutterMethodCall
- (FlutterMethodCall *)decodeMethodCall:(NSData*)methodCall;

//将正常响应结果result编码为二进制
-(NSData*)encodeSuccess Envelope:(id _nullable)result;

//将错误响应提示编码为二进制NSData
-(NSData*)encodeErrorEnvelope:(FlutterError*)error;

//将二进制数据NSData解码,失败返回FlutterError
-(id _nullable)decodeEnvelope:(NSData*)envelope;

FlutterMethodCall 代表从 Flutter端发起的方法调用。方法调用包括:方法名、方法参数以及方法返回结果。因此和FlutterMessageCodec 相比,FlutterMethodCodec 中多了两个处理调用结果的方法:

  • 方法调用成功:使用 encodeSuccessEnvelope:编码result。
  • 方法调用失败:使用encodeErrorEnvelope:编码FlutterError。

decodeEnvelope:方法则用与解码iOS平台代码调用Dart中方法的结果。比如通过FlutterMethodChannel调用Flutter中的方法,且获得其返回结果。