??19年初,準(zhǔn)備嘗試 Flutter 實現(xiàn)部分業(yè)務(wù)頁面士聪,一個因為 Native 主工程已經(jīng)較為龐大锦援,一個因為目前只有少部分人投入 Flutter 的嘗試,因此不可能重建一個 Flutter 工程來實現(xiàn)整個主工程剥悟,而且因為少部分人嘗試灵寺,所以也不可能每個人都安裝 Flutter 環(huán)境曼库,所以,在 Native 工程中引入 Flutter 過程中略板,最好能像引入 其它第三方組件一樣通過 pod 引入 Flutter毁枯,而其他非 Flutter 的業(yè)務(wù)開發(fā)人員無需安裝 Flutter 環(huán)境來實現(xiàn)無痕的嵌入使用
?? 接下來我們就關(guān)于 Native 工程中引入 Flutter 的一些做法進行簡單說明
-
簡單的實現(xiàn)
- 新建了一個
flutter_app
的Flutter 工程
,用來實現(xiàn)相關(guān)的 flutter 業(yè)務(wù)叮称,它的目錄結(jié)構(gòu)如下
?? 由 Flutter 目錄分別包含兩個平臺的 Native 工程的目錄組成种玛,我們通過flutter build ios --release
后可以查看到 ios 的 Flutter 目錄下包含了一些構(gòu)建運行后的產(chǎn)物:
App.framework
- dart代碼生成
Flutter.framework
- 引擎
(這塊資源在新的Fluterr版本中包含在App.framework中)flutter_assets
- 相關(guān)資源
??而這也正是 Navtive 工程引入 Flutter 需要的依賴,所以很容易想到收集這些中間產(chǎn)物作為組件內(nèi)容瓤檐,并通過 pod 引入即可
????????以上的產(chǎn)物均為 flutter 平臺相關(guān) dart 代碼資源赂韵,若是引入 plugin,那理應(yīng)還有 plugin 對應(yīng) Navtive 平臺的代碼挠蛉,通過圖上目錄右锨,.flutter-plugins
即項目引用的 plugins 及對應(yīng)本地路徑,也就是說這一步我們目前可知需要的內(nèi)容如下
.flutter-plugins - 引用的 plugins 及對應(yīng)本地路徑
App.framework - dart代碼生成
Flutter.framework - 引擎
flutter_assets - 相關(guān)資源
Source - Flutter 工程的原生代碼碌秸,一些 channel 代碼绍移,混合棧的構(gòu)建代碼
- 新建一個
product_flutter
的中間產(chǎn)物模塊,Flutter 編譯過后的產(chǎn)物
讥电、plugin Navtive平臺代碼
蹂窖,Flutter 工程的原生代碼 - Source
進行收集放置 product_flutter 這個模塊中,然后上傳至 git 倉庫恩敌,目錄如下
.podspec
需要??的一個就是 .flutter-plugins
,pluginspodhelper.rb
,plugins
在pod下載后不要刪除瞬测,否則工程項目分析及pod plugin會缺少文件,所以.podspec
的內(nèi)容如下:
# MARK: converted automatically by spec.py. @liya
Pod::Spec.new do |s|
s.name = 'product_flutter'
s.version = '1.0'
s.description = 'product_flutter纠炮,喵喵'
s.license = 'MIT'
s.summary = 'product_flutter'
s.homepage = 'https://xxx'
s.authors = { 'Liya' => 'xxx@qq.com' }
s.source = { :git => '.../product_flutter', :branch => 'master' }
s.ios.deployment_target = '8.0'
s.vendored_frameworks = 'Flutter.framework', 'App.framework'
s.resources = 'flutter_assets'
s.source_files = 'Source/**/*.{h,m,c}'
s.preserve_paths = 'pluginspodhelper.rb', '.flutter-plugins', 'Plugins/**/*.{*}'
end
收集腳本較為簡單月趟,這里就發(fā)下 plugins
的收集部分
flutter_res_plugins=$PWD/.flutter-plugins
......
while read -r line
do
if [[ ! "$line" =~ ^// && ! "$line" =~ ^# ]]; then
array=(${line//=/ })
iosClasses=${array[1]}ios/Classes
plugin=$PWD/Plugins/${array[0]}
if [ -d $plugin ]; then
rm -rf $plugin
fi
if [ -d $iosClasses ]; then
mkdir -p $plugin
cp -rf $iosClasses $plugin
// 這里是為了解決 plugins 相關(guān)的依賴傳遞,所以把.podspec也進行包含
podspec=${array[1]}ios/${array[0]}.podspec
if [ -d $iosClasses ]; then
cp -f $podspec $plugin
podspec=$plugin/${array[0]}.podspec
echo "刪除 s.dependency 'Flutter' in $podspec"
sed -i '' -e "s/s.dependency 'Flutter'//g" $podspec
fi
echo "plugin $plugin"
echo "iosClasses $iosClasses"
fi
fi
done < $flutter_res_plugins
- 在前兩步完成后恢口,考慮到參與 Flutter 項目的人員可能 Flutter 環(huán)境不一致等情況孝宗,還有就是每次都需要手動 build 并更新上傳至 product_flutter 倉庫,所以借助 ci 完成部分手動工作耕肩,同時 ci Flutter 環(huán)境單一因妇,可以確保打出來的中間產(chǎn)物環(huán)境一致
?? 在 ci 創(chuàng)建 test_flutter 的任務(wù),設(shè)定腳本每天在凌晨進行一次打包后自動上傳中間產(chǎn)物至 product_flutter(也可手動去 ci 點擊進行打包)猿诸,整個腳本目前很簡單婚被,主要也就是
#...
export PATH=/usr/local/bin:$PATH
export PATH=$PATH:$HOME/XXX/flutter/bin
echo "flutter begin"
flutter packages get
echo "pod install or update"
cd ios
if [ ! -d "$PWD/Pods"]; then
pod install
else
pod update
fi
cd -
echo "flutter release -- "
flutter clean
flutter build ios --release
# 接下來其它操作也僅僅就是將產(chǎn)物拷貝打tag并上傳
- 而在需要使用 Flutter 的工程中,調(diào)用較為簡單梳虽,但是同時要注意到
plugin 的 pod 使用
# pod product_flutter址芯,這里進行本地實驗,放git上一樣
pod 'product_flutter',:git => '../product_flutter'
# 產(chǎn)物收集時同時也收集了.flutter-plugins
# 使用時依然分析一次 .flutter-plugins 并依次對 plugin 進行 pod 引用
flutter_pod_fold ||= File.join(__dir__,"Pods","product_flutter")
flutter_plugin_rb ||= File.join(flutter_pod_fold,"pluginspodhelper.rb")
if File.exists? flutter_plugin_rb
load flutter_plugin_rb
pod_KV_file(flutter_pod_fold)
end
其中 pluginspodhelper.rb
腳本主要內(nèi)容如下
....
def pod_KV_file(pod_file)
# If this wasn't specified, assume it's two levels up from the directory of this script.
print "進行 pod plugin "
flutter_pod_path ||= File.join(pod_file)
plugin_pods_file = parse_KV_file(File.join(flutter_pod_path, '.flutter-plugins'))
plugin_pods_file.map { |r|
print "plugin_pods_file = ",r
pod r[:name], :path => File.join(flutter_pod_path, 'Plugins', r[:name])
}
end
-
題外話
- 目前是通過
ci 實現(xiàn)定時任務(wù)
,打包后自動上傳中間產(chǎn)物谷炸,也可以通過git hook
判斷flutter_app
提交的新代碼删顶,然后自動觸發(fā)ci
進行flutter_app
打包
采取哪種方式或者打包頻次根據(jù)自身需要即可 - flutter 打包在不同模式下的中間產(chǎn)物具有差異
a.debug
模式下有JIT支持
(所以支持熱重載 HotReload),App.framework
只有幾個簡單的 API淑廊,其 Dart 代碼生成標(biāo)記化的源代碼,運行時編譯特咆,解釋執(zhí)行季惩,存在于flutter_assets
的kernel_blob.bin
文件里
b.release
模式下是AOT支持
,App.framework
是所有的 dart 代碼(業(yè)務(wù)代碼腻格,第三方 package 代碼画拾,及所依賴的 flutter 框架代碼等等)編譯所成,Flutter.framework
則是Flutter 架構(gòu)
中的engine
部分菜职,flutter_assets
是圖片青抛,字體等相關(guān)資源
-
莫名其妙
?? 也不懂為啥突然想在大年初一寫這個,也沒完全規(guī)劃好酬核,寫的也很簡單蜜另,不過本身遠程依賴這一步并不復(fù)雜~~權(quán)當(dāng)作新年開關(guān)好了~~ON 一下~~
=======================后續(xù)的一點補充===============================
-
Native 主工程動態(tài)引入Debug及Release環(huán)境的中間產(chǎn)物包
因為如題外話所說,flutter
打包的產(chǎn)物在不同模式下是有差異的嫡意,所以導(dǎo)致在開發(fā)模式中需要用的Debug举瑰,但是打包上架需要Release,而作為Flutter開發(fā)人員蔬螟,有時有希望能在本地主工程引用中看效果此迅,因此可能需要手動切換等,耗時耗力旧巾,這真是萬萬不能忍呀~~耸序,所以對這塊的腳本及流程進行了一些稍稍優(yōu)化
1 . 收集產(chǎn)物時,進行兩模式的分開收集鲁猩,一是 Debug 模式坎怪,一是 Release 模式
flutter build ios --no-codesign --debug
//跟以上一樣收集對應(yīng)的真機的debug產(chǎn)物
flutter build ios --simulator --debug
//同上并合并
lipo -create $res_dir/App.framework/App $target_debug_dir/App.framework/App -output $target_debug_dir/App.framework/App
....
Release 模式的
flutter build ios --no-codesign --release
//收集Release產(chǎn)物,并注意要將注冊文件一起收集到Source中
cp {$source_runner_res_dir/GeneratedPluginRegistrant.h,$source_runner_res_dir/GeneratedPluginRegistrant.m} $target_source_dir
- 收集產(chǎn)物后,就是pod的相關(guān)操作了
1). 將 Flutter Debug 及 Release 產(chǎn)物分別對應(yīng)一個 podspec 文件疾棵,而個人工程中相關(guān)的原生代碼對應(yīng)一個 podspec 文件戈钢,引用如下,即可解決不同模式下引用是尔,方便了手動操作
( PS 這個如果有更好的 framework search 路徑設(shè)置及 flutter_assets 資源路徑設(shè)置方法殉了,請留言,之前嘗試過直接在 podfile 中設(shè)置拟枚,依然有各種問題 )
pod 'LYFlutter', :git => "...", :branch => branch
pod 'FlutterFrame_Debug', :git => "...", :branch => branch, :configurations => 'Debug'
pod 'FlutterFrame_Release', :git => "...", :branch => branch, :configurations => 'Release'
當(dāng)然薪铜,在實際使用中均放置在 FlutterPodHeleper.rb 腳本中众弓,并在其中下載 .flutter-plugins文件,并解析并pod相關(guān)文件
# flutter遠程依賴 - release的產(chǎn)物
# $flutter_pod_path : 工程 pods 的路徑
# $flutter_branch : LYFlutter的分支隔箍,未設(shè)置默認 "master"
$flutter_pod_path = __dir__+"/Pods"
eval(File.read(File.join(__dir__, 'FlutterPodHelper.rb')), binding)
# flutter本地依賴
# $flutter_application_path : flutter工程的路徑
$flutter_application_path = "/Users/.../Liya_flutter"
eval(File.read(File.join(__dir__, 'FlutterPodHelper.rb')), binding)
post_install hook
post_install do |installer|
#調(diào)用配置 - bitcode設(shè)置
update_flutter_configs(installer, $flutter_application_path)
end
- FlutterPodHeleper.rb 腳本實現(xiàn)主要如下谓娃,其實就是之前的 pluginspodhelper .rb 腳本,改后承接較多的內(nèi)容
# 遠程的plugins文件解析
def pod_remote_flugins_file(plugin_helper_local_path, pod_file)
# If this wasn't specified, assume it's two levels up from the directory of this script.
print "解析并寫入pod plugin \n"
plugin_pods_file = parse_KV_file(File.join(plugin_helper_local_path))
plugin_pods_file.map { |r|
print "plugin_pods_file = ",r[:name]," \n"
pod r[:name], :path => File.join(pod_file, 'LYFlutter/Plugins', r[:name])
}
end
# 遠程的plugins文件下載并解析
def down_remote_plugins_file(branch, pod_file)
lyflutter_url = "https://.../LYFlutter"
lyflutter_git_url = lyflutter_url+".git"
plugin_helper_url = lyflutter_url+"/raw/"+branch+"/PluginTool/.flutter-plugins"
plugin_helper_local_path = './.flutter-plugins'
print "下載 plugins 解析文件 \n"
download = open(plugin_helper_url)
IO.copy_stream(download, plugin_helper_local_path)
pod 'LYFlutter', :git => lyflutter_url, :branch => branch
pod 'FlutterFrame_Debug', :git => lyflutter_url, :branch => branch, :configurations => 'Debug'
pod 'FlutterFrame_Release', :git => lyflutter_url, :branch => branch, :configurations => 'Release'
pod_remote_flugins_file(plugin_helper_local_path, pod_file)
print "刪除下載 plugins 相關(guān)文件 \n"
File.delete(plugin_helper_local_path)
end
本地的引用
# 本地的引用
def local_remote_plugins_file(flutter_application_path)
native_application_path = File.join(flutter_application_path, 'ios')
framework_dir = File.join(native_application_path, 'Flutter')
pod 'Flutter', :path => native_application_path
pod 'LYFlutter', :path => native_application_path
symlinks_dir = File.join(native_application_path, '.symlinks')
FileUtils.mkdir_p(symlinks_dir)
plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
plugin_pods.map { |r|
symlink = File.join(symlinks_dir, 'plugins', r[:name])
FileUtils.rm_f(symlink)
File.symlink(r[:path], symlink)
pod r[:name], :path => File.join(symlink, 'ios')
}
end
如上基本就是引用腳本的主要代碼了蜒滩,遠程依賴及產(chǎn)物收集腳本及目錄結(jié)構(gòu)詳情點??
這文章組織上自己沒做好滨达,就將就吧~~??