研究Flutter端端续续也已经半年多了,从1.5跟到1.9,但都是浅尝辄止,没有深入思考,正巧最近公司在大力搞这个东西,就又拿了起来。最近主要思考的是如何将Flutter放到自己的私有仓库库里,方便CI打包,而不是官网推荐的方式本地依赖。为什么要这么做呢?本地依赖有几个弊端。
- CI打包的问题,现在的CI打包已经成熟了,为了官网的混编,CI适配一下,成本较高,也不符合设计方式
- Native开发的时候不会关心Flutter工程,为了开发Native,本地搭建Flutter开发环境也不符合
想来想去也就只能研究研究远程依赖的方式。下面浅谈一下最近的一些成果,仅限于iOS,Android同学不要介意,不太会Android。
Flutter官网混编方式
Add Flutter to existing apps-master分支
官网现在的推荐方式比以前已经方便了很多了,而且已经到了Preview版本,相信不久的将来,我们会在稳定分支中使用它。
简单讲述下官网的混编方式。
Create a Flutter module
首先进入你的项目目录,键入下面命令:
$ cd some/path/
$ flutter create -t module my_flutter
完成后,目录结构如下面:
some/path/
my_flutter/
lib/main.dart
.ios/
MyApp/
MyApp/
AppDelegate.h
AppDelegate.m (or swift)
:
Add your Flutter app to your Podfile
- Add the following lines to your Podfile:
flutter_application_path = 'path/to/my_flutter/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
- For each Xcode target that needs to embed Flutter, call
install_all_flutter_pods(flutter_application_path).
target 'MyApp' do
install_all_flutter_pods(flutter_application_path)
end
target 'MyAppTests' do
install_all_flutter_pods(flutter_application_path)
end
- Run
pod install.
每次你添加Flutter plugin在some/path/my_flutter/pubspec.yaml,都需要在some/path/my_flutter执行flutter pub get以便通过podhelper.rb脚本刷新插件列表,然后在iOS项目目录再次执行pod install。
4. Disable bitcode for your targets
Flutter不支持bitcode,将iOS的targetENABLE_BITCODE设置为No。
进行以上操作后,iOS项目就集成了Flutter模块,还有具体细节,如何运行Flutter模块,AppDelegate需要进行部分修改,就不再赘述,大家参考官方文档Add Flutter to existing apps
Flutter依赖分析
混编依赖产物分析
打开Pod工程,会发现一共依赖了三个模块:
- Flutter模块,包含Flutter.framework:Flutter库和引擎
- my_flutter模块,App.framework:dart业务源码相关文件
- FlutterPluginRegistrant模块,
Flutter Plugin:编译出来的各种plugin的framework,这里描述的可能不正确,该模块应该是用来注册flutter plugin,由于demo工程没有flutter plugin,所以Flutter Plugin的模块,所以如果flutter项目有plugin,应该会有更多的模块
Podfile脚本分析
flutter_application_path = '../my_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
第一句,声明了flutter项目的地址,File.join拼接目录,目录为../my_flutter/.ios/Flutter/podhelper.rb,通过ruby的load方法将该目录podhelper.rb引入执行环境。
那么podhelper.rb中写了哪些东西呢?进入该文件的目录下查看,内容大致如下:
def install_all_flutter_pods(flutter_application_path = nil)
...
end
def install_flutter_engine_pod
...
end
def install_flutter_plugin_pods(flutter_application_path)
...
end
def install_flutter_application_pod(flutter_application_path)
...
end
都是方法,那么有什么用?根据官方文档指示,iOS工程需要flutter模块的target都需要在podfile中键入install_all_flutter_pods(flutter_application_path),这不就是podhelper.rb中的方法并且传入podfile声明的flutter_application_path嘛!再看该方法:
# Install pods needed to embed Flutter application, Flutter engine, and plugins
def install_all_flutter_pods(flutter_application_path = nil)
flutter_application_path ||= File.join('..', '..')
install_flutter_engine_pod
install_flutter_plugin_pods(flutter_application_path)
install_flutter_application_pod(flutter_application_path)
end
就是每个target下安装所需要的Flutter application, Flutter engine, and plugins,继续看着里的三个方法:
def install_flutter_engine_pod
...
pod 'Flutter', :path => relative.to_s, :inhibit_warnings => true
end
def install_flutter_plugin_pods(flutter_application_path)
...
pod 'FlutterPluginRegistrant', :path => File.join(relative, 'FlutterPluginRegistrant'), :inhibit_warnings => true
...
end
def install_flutter_application_pod(flutter_application_path)
...
pod 'my_flutter', :path => relative.to_s, :inhibit_warnings => true
...
end
最终就是在target下执行各个flutter模块的pod操作,路径为本地路径。
综上就是官方混编的方式,其中有些细节没有进行梳理,比如plugin的引入,其实也是pod的操作,由于demo工程没有引入插件,暂时不进行讨论。
Flutter远程依赖的尝试
由于Flutter官方依赖的方式Pod本地引入,所以我猜想也可以Pod远程引入,目光集中在了Flutter.podspec、my_flutter.podspec、FlutterPluginRegistrant.podspec三个podspec文件中,进过pod install日志分析,三个podspec定位在:
Flutter.podspec在../my_flutter/.ios/Flutter/enginemy_flutter.podspec在../my_flutter/.ios/FlutterFlutterPluginRegistrant.podspec在../vmovier_flutter/.ios/Flutter/FlutterPluginRegistrant
所以尝试把这三个podspec复制在了flutter项目的根目录下:
some/path/
my_flutter/
lib/main.dart
.ios/
Flutter.podspec
my_flutter.podspec
FlutterPluginRegistrant.podspec
并且对根目录下的podspec进行了部分修改,以便能正确的索引到响应的代码,以Flutter.podspec为例:
Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.description = <<-DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
DESC
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :git => 'git@git.lug.ustc.edu.cn:Sir.heng/vmovier.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.vendored_frameworks = '.ios/Flutter/engine/Flutter.framework'
end
其中原来的s.vendored_frameworks为Flutter.framework改为了.ios/Flutter/engine/Flutter.framework,这样pod进行远程引入的时候就可以正确索引到Flutter.framework了,my_flutter.podspec、FlutterPluginRegistrant.podspec这两个修改也是大同小异。
首先对iOS Native进行pod本地引入三个模块:
pod 'Flutter', :path => '../my_flutter'
pod 'my_flutter', :path => '../my_flutter'
pod 'FlutterPluginRegistrant', :path => '../my_flutter'
pod install后运行Native项目,成功跑起来了,说明这样修改没问题。
下面就把Flutter项目推到远程就行了,然后进行远程引入:
pod 'Flutter', :git => 'git@xxxxxx/my_flutter.git'
pod 'my_flutter', :git => 'git@xxxxxx/my_flutter.git'
pod 'FlutterPluginRegistrant', :git => 'git@xxxxxx/my_flutter.git'
pod install后运行Native项目,成功跑起来了,说明这样修改没问题。
需要注意的是,每次发布Flutter项目都需要进行,flutter build ios操作,以便将Flutter.framework和my_flutter.framework编译出来。
总结&现有问题
这样依赖后,项目可以正常运行,但是有几个问题:
- Flutter.framework 300M+,不知道打出包来会如何,待验证以及优化
- Flutter项目不能依赖Plugin,因为该方案,没有将Plugin引入,Plugin官方的依赖方式也是pod本地引入,但是多个Plugin会生成多个
Plugin.podspec,改方案不能很好的支持,所以对于混编通信以及调用原生Api的需求,建议使用FlutterMethodChannel的方式,通信代码由Native进行编写与维护。
以上就是最近的总结,希望各位看官批评指正,互相交流