前言


下面的内容是最近在使用Flutter和我们自己项目进行混编时候的一些总结以及自己踩的一些坑,处理完了就顺便把整个过程以及一些我们可能需要注意的点全都梳理出来,希望对有需要的小伙伴有点帮助,也方便自己后续的查看。

一:混编具体步骤以及需要注意的问题


1:创建Flutter项目  (切记:下面任何命令执行出错基本上都是Flutter环境有问题,多执行 Flutter doctor 检查)

这里需要我们留意的就一点, 创建的Flutter项目的文件层级和你想混编的原生项目要同级,就像下面这样:

终端命令行如下: flutter_module:你自己的项目名称,自己定义。-t 和 --template 一样,别纠结。

flutter create -t module flutter_module

还是前面开头说的,有问题多执行 flutter dotcor检查,要是没有问题,正确创建成功之后是下面的情况:(我临时在桌面创建的,请忽略位置)

2:通过pod将Flutter模块导入项目

我们在我们项目的podfile文件中加入下面两句:

flutter_application_path = '../flutter_mixed'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

注意: flutter_application_path 后面的是你自己Flutter项目的名称。flutter_application_path为Flutter模块相对于podfile文件的位置。

在target种加入下面这句

install_all_flutter_pods(flutter_application_path)

我这里刚好有一份写demo时候的podfile文件,代码全都给出来,方便也想demo尝试的小伙伴直接复制,节省时间。

platform :ios, '14.0'
source 'https://cdn.cocoapods.org/' use_frameworks!
#use_modular_headers! # 忽略引入库的所有警告
inhibit_all_warnings! # [!] Could not automatically select an Xcode project. Specify one in your Podfile like so:
# project 'path/to/Project.xcodeproj' # [!] `xcodeproj` was renamed to `project`. Please update your Podfile accordingly. xcodeproj 'flutter_mixed_ios.xcodeproj' flutter_application_path = '../flutter_mixed'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') target 'flutter_mixed_ios' do install_all_flutter_pods(flutter_application_path) ####
pod 'AFNetworking'
pod 'WoodPeckeriOS' end

3:接下来就是 pod install

4:关于原生项目的配置更改以及问题解释

<1> Flutter混编项目是不支持Bitcode的,具体Bitcode代表的是什么,这个大家可以翻以前我的文章:

<2> Build Phases 添加 Script 具体的操作如下所示:

添加下面内容:

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

注意:单纯这样添加之后编译大概率是不通过的,主要问题就是集中的 FLUTTER_ROOT 这个点上

/packages/flutter_tools/bin/xcode_backend.sh: No such file or directory

我们就把注意力放在 No such file or directory 上,别走别的岔路。解决上面这问题的方法就是在你的项目中指定一下FLUTTER_ROOT的具体路径,让不再No such file or directory就OK了

具体的做法是在 Build Settings中找到 User-Defined 添加 FLUTTER_ROOT 和 FLUTTER_APPLICATION_PATH

FLUTTER_ROOT是我Flutter环境所在的具体位置 FLUTTER_APPLICATION_PATH 是我这个Flutter项目所在的路径,当然我们还有更靠谱的查找这两路径位方法。

终端进入自己flutter项目,按照下面路径/.ios(隐藏文件)/Flutter/Generated.xcconfig  我们open Generated.xcconfig 文件就,在最上面就可以看到我们需要的FLUTTER_ROOT 和 FLUTTER_APPLICATION_PATH 。

经过上面的处理,我们的 No such file or directory的问题就解决了,最后我们说说 xcode_backend.sh ,其实关于它我想表达的就只有一点,就这个脚本的作用到底有哪些,他能帮我们完成什么工作呢?

前面的疑问,这篇文文章给出了具体的分析 #Flutter之ios脚本 xcode-backend.sh文件分析#,文章逐句分析了我们xcode_backend.sh脚本的代码,也就间接的阐述了它的作用。有兴趣的朋友可以好好了解一下。

经过脚本的处理,有这样一种场景,我们在开发的时候假如修改了一些涉及到混编消息传递的内容(任何Flutter内容都可以),我们在Flutter这边修改了代码,这时候你可以直接运行Xcode查看更改的内容是否正确,里面具体的工作我们在运行Xcode它在执行xcode_backend.sh脚本的时候已经帮我们处理了。当然正常Flutter修改的内容我们运行Flutter项目查看,在原生传递消息给Flutter的时候需要我们运行iOS项目,就打个上面的比方,理解知道就可以了。

至此,你的iOS和Flutter混编的代码是可以正常运行起来的了。

5:Local Network Privacy Permissions

这个问题我们在查看Flutter官方文档进行学习的时候肯定是可以看到的。  官方解释传送门

在你运行混编iOS项目的时候,你不处理这个问题就可以看到下面内容的日志:

Failed to register observatory port with mDNS with error -65555. On iOS 14+, local network broadcast in apps need to be declared in the app's Info.plist. Debug and profile Flutter apps and modules host VM services on the local network to support debugging features such as hot reload and DevTools. To make your Flutter app or module attachable and debuggable, add a '_dartobservatory._tcp' value to the 'NSBonjourServices' key in your Info.plist for the Debug/Profile configurations.

官方也给我们做出了提醒以及解释说明:

【 在iOS 14和更高版本,在你的应用程序的调试版本中启用Dart组播DNS服务,以添加调试功能,如热加载和DevTools via flutter attach。注意:该服务不能在你的应用的Release版本中启用,否则你可能会遇到应用商店拒绝。一种方法是维护应用程序信息的一个单独副本。每个构建配置的Plist。下面的说明假设默认的Debug和Release。根据应用程序的构建配置,根据需要调整名称 】

我自己还是按照官方给的的处理方法处理的

首先还是处理我们的plist文件,把它处理成debug和release两个模式的,我们一旦改了它们。在 build settings 中一定要改动,不然编译肯定过不了的!具体的操作如下图:

    

Build Settings Info.plist 这里我们添加的是 Info-$(CONFIGURATION).plist Debug和Release环境的让它们根据自己的配置内容读取。

接下来就是再Debug环境中的配置问题,这里主要有两点:

1、Privacy - Local Network Usage Description 填写的 Allow Flutter tools on your computer to connect and debug your application.This prompt will not appear on release builds. ,当然我是写demo随便写的,具体的在自己项目中需要自己填写,这个权限在iOS14之后审核比较严格,大家需要注意,要使用到得描述清楚,避免审核被拒绝,但具体的苹果什么加强这块的审核,我们大致了解下这个权限的用处就理解了。

【 因为在过去的 iOS 版本中,应用可以随意扫描本地网络中的设备,因此应用就可以很轻松地得到本地网络里所有设备的名称和MAC地址。MAC地址是一种确认网络设备位置的地址,每个网卡都有一个唯一的MAC地址,加上MAC地址也具有唯一性,设备厂商会按照一定的规律分配MAC,所以不同的局域网都是独一无二可以识别的。这样就通过MAC地址和设备的名字以生成一个特定的「指纹」,持续地、跨应用地、跨设备地跟踪用户的行为,并对用户画像持续进行调整。就大部分应用而言,它们都不需要给本地网络权限。因为它们没有功能会使用到本地网络,请求这个权限的主要目的就是为了跟踪用户并推送广告。】

2、Bonjour services  填写的  _dartobservatory._tcp

最后还剩一点就是把Copy Bundle当中的Info-Release.plist进行一个删除。下面图片中的内容我是已经删除了的:

       经过上面的处理之后,Local Network Privacy Permissions 这个问题我们就应该是解决了!

二:原生与Flutter通信


首先Flutter为我们提供了以下几种原生和Flutter之间通信的方式:

  • FlutterBasicMessageChannel 双向通道,iOS和Flutter都可以主动向对方传递消息,最简单的传递数据方式。
  • FlutterMethodChannel 也是双向通信,它的使用和FlutterBasicMessageChannel基本上一致,不同的点在于FlutterMethodChannel可以自定义Channel的name。
  • FlutterEventChannel 用于事件流的发送(event streams), 属于持续性的单向通信, 只能是iOS端主动调用, 常用于传递原生设备的信息, 状态等, 比如电池电量, 远程通知, 网络状态变化, 手机方向, 重力感应, 定位位置变化等等。

具体的它们三者的使用我们就不在很具体的说了,我们就从FlutterMethodChannel这个方法入手,简单的看一下Flutter给iOS发送消息以及iOS给Flutter发送消息时候具体的代码执行是什么样子的,具体的过程当中我们又遇到了那些问题,我们也简要的进行一个分析。

1、Flutter给iOS发送消息

iOS端的代码,下面代码大致逻辑是iOS端接收到Flutter发送的channel name为MixChannelName.backToNative,消息名称为 MixChannelMethod.iOSBack,执行返回上个控制器。

/// MixFlutterViewController 继承与 FlutterViewController
extension MixFlutterViewController{ /// 返回事件
func channelBack() {
/// MixChannelName.backToNative 字符串channel name
self.flutterMethodChannel = MixFlutterMethodChannel.init(name: MixChannelName.backToNative, binaryMessenger: self.engine!.binaryMessenger)
self.flutterMethodChannel!.setMethodCallHandler { [weak self] (call:FlutterMethodCall,result:@escaping FlutterResult) in
/// 返回上一个页面
/// MixChannelMethod.iOSBack 字符串返回方法名称
if call.method == MixChannelMethod.iOSBack{ self?.navigationController?.popViewController(animated: true)
self?.flutterMethodChannel = nil
} else { result(FlutterMethodNotImplemented)
}
}
}
} class MixFlutterMethodChannel: FlutterMethodChannel { deinit {
debugPrint("MixFlutterMethodChannel - deinit")
}
}

我们再看看Flutter端的发送代码是怎么处理的:

///  前面定义一个MethodChannel 名称为flutter_backToNative 和iOS端的需要保持一致
static const _messageChannel = MethodChannel("flutter_backToNative"); /// 然后在你需要发送消息的地方调用
_messageChannel.invokeMethod("backToNative");

经过上面的处理之后,我们的iOS端是能够正接受到Flutter发送的消息的。

2、iOS给Flutter发送消息

Flutter端的代码,还是之前的_messageChannel这个渠道,直接调用setMethodCallHandler设置接收到消息的处理函数。

/// 建立和原生通讯的渠道
_messageChannel.setMethodCallHandler((call) => handleMessage(call.arguments)); /// 处理消息的方法
Future handleMessage(String message) async { print(message);
}

iOS端的代码如下,flutterMethodChannel还是我们刚开始创建的渠道

/// 发送普通消息
/// - Parameter stringParams: stringParams description
func sendMessageWithString(_ stringParams:String){
/// MixChannelMethod.goodsId 调用的方法名称
self.flutterMethodChannel!.invokeMethod(MixChannelMethod.goodsId, arguments: stringParams)
}

注意点: 在使用FlutterMethodChannel进行双向通信的时候,尤其需要注意的是iOS端和Flutter端的渠道Channel的name一定要保持一致!

疑惑点:我在MixFlutterViewController的deinit方法中加入了日志,然后综合上面的MixFlutterMethodChannel中deinit的日志,得出一个有点不理解的点,主要疑问如下面所示是在flutterMethodChannel的创建方式上。

         /*
"MixFlutterMethodChannel - deinit"
2022-05-08 22:28:26.159278+0800 flutter_mixed_ios[70375:6110936] flutter: 10086
"MixFlutterViewController - deinit"
"MixFlutterMethodChannel - deinit"
2022-05-08 22:28:35.960283+0800 flutter_mixed_ios[70375:6110936] flutter: 10086
"MixFlutterMethodChannel - deinit"
"MixFlutterViewController - deinit"
"MixFlutterMethodChannel - deinit"
*/
/// 使用该方法创建后 在Flutter发送消息返回 打印日志如上面注释
self.flutterMethodChannel = MixFlutterMethodChannel.init(name: MixChannelName.backToNative, binaryMessenger: self.engine!.binaryMessenger,codec: FlutterStandardMethodCodec.sharedInstance()) /*
2022-05-08 22:31:21.842965+0800 flutter_mixed_ios[70389:6112382] flutter: 10086
"MixFlutterViewController - deinit"
*/
/// 使用该方法创建后 在Flutter发送消息返回 打印日志如上面注释
self.flutterMethodChannel = MixFlutterMethodChannel.init(name: MixChannelName.backToNative, binaryMessenger: self.engine!.binaryMessenger)

总结:经过上面的内容,关于iOS和Flutter的混编的一些东西就都介绍完毕了,疑问点还是存在,等后面找到具体的答案之后我会补充在文章后面。要是对上面内容有什么疑问,可以留言或者私信我,可以换个方式具体的沟通。

Flutter和iOS混编详解的更多相关文章

  1. 转载]IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本 )

    原文地址:IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本作者:佐佐木小次郎 因为最近项目上要用有关LBS的功能.于是我便做一下预研. 一般说来LBS功能一般分为两块:一块是地理 ...

  2. iOS中-Qutarz2D详解及使用

    在iOS中Qutarz2D 详解及使用 (一)初识 介绍 Quartz 2D是二维绘图引擎. 能完成的工作有: 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成 ...

  3. iOS 2D绘图详解(Quartz 2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)

    前言:一个路径可以包含由一个或者多个shape以及子路径subpath,quartz提供了很多方便的shape可以直接调用.例如:point,line,Arc(圆弧),Curves(曲线),Ellip ...

  4. iOS开发——Block详解

    iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...

  5. iOS开发:详解Objective-C runTime

    Objective-C总Runtime的那点事儿(一)消息机制 最近在找工作,Objective-C中的Runtime是经常被问到的一个问题,几乎是面试大公司必问的一个问题.当然还有一些其他问题也几乎 ...

  6. iOS应用开发详解

    <iOS应用开发详解> 基本信息 作者: 郭宏志    出版社:电子工业出版社 ISBN:9787121207075 上架时间:2013-6-28 出版日期:2013 年7月 开本:16开 ...

  7. 了解iOS消息推送一文就够:史上最全iOS Push技术详解

    本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ...

  8. iOS开发者证书-详解

    iOS开发者证书-详解/生成/使用 本文假设你已经有一些基本的Xcode开发经验, 并注册了iOS开发者账号. 相关基础 加密算法 现代密码学中, 主要有两种加密算法: 对称密钥加密 和 公开密钥加密 ...

  9. iOS开发-Runtime详解

    iOS开发-Runtime详解 简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [recei ...

随机推荐

  1. thymeleaf调用静态类

    为啥需要调用本地静态方法 Thymeleaf模板里面有时候需要使用本地静态常量,要是每次都在控制层传一遍很麻烦 我们需要在Thymeleaf模板我们使用比如 1代表一年级,2代表二年级,3代表三年级等 ...

  2. 什么是切点JoinPoint?

    程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理. 在 Spring AOP 中, join point 总是方法的执行点.

  3. apollo规划控制视频-13 motion planning with autonomous driving

  4. 180度\360度sg90舵机的使用及代码程序

    大部资料都是在网上找到网友大神所共享的,在网上找了几种舵机的,刚接触有点懵,之后找得多了就理解了,想要控制一个硬件就要先了解这个硬件.这里有介绍180度舵机和360度舵机的具体使用,有网上大神的程序, ...

  5. python学习笔记(一)——Python基础

    一.python 基础语法 python 的解释器在启动时会自动加载一个内建的模块,因此我们在使用 print().input()等函数时不用导入其他模块文件. 基本语法: 每条语句结尾没有分号 定义 ...

  6. 让你熟知jquery见鬼去吧

    $是jquery最具代表的符号,当然php也是,但是二者不能同日而语;不得不说jquery的选择器是大家赞不绝口的,在它1.x版本中对ie兼容性是最好的,这要归功于$选择器; 现在呢,html5的降临 ...

  7. 有关placeholder在ie9中的一点折腾

    有关placeholder在ie9中的一点折腾. placeholder属性定义: placeholder 属性规定可描述输入字段预期值的简短的提示信息(比如:一个样本值或者预期格式的短描述). 问题 ...

  8. 原型模式故事链--new一个对象的过程

    上一个总标题:https://segmentfault.com/a/11...提问:你有对象了吗?答:没有.笨!new一个不就好了吗! 问题点:为什么我要理解new一个对象的过程?答:不理解这个过程, ...

  9. java中请给出例子程序:找出n到m之间的质数。

    9.1 找出100到200之间的质数.  public class Test {     public static void main(String[] args){         for (in ...

  10. 分库分表实现方式Client和Proxy,性能和维护性该怎么选?

    大家好,我是[架构摆渡人],一只十年的程序猿.这是分库分表系列的第一篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友. 其实这个系列有录过视频给大家学习,但很多 ...